注解:
注解目前非常的流行,很多主流框架都支持注解,而且自己編寫代碼的時候也會盡量的去用注解,既方便,也讓代碼更加簡潔。
注解雜談:
Annontation是Java5開始引入的新特征,中文名稱叫注解。它提供了一種安全的類似注釋的機制,用來將任何的信息或元數(shù)據(jù)(metadata)與程序元素(類、方法、成員變量等)進行關(guān)聯(lián)。為程序的元素(類、方法、成員變量)加上更直觀更明了的說明,這些說明信息是與程序的業(yè)務邏輯無關(guān),并且供指定的工具或框架使用。Annontation像一種修飾符一樣,應用于包、類型、構(gòu)造方法、方法、成員變量、參數(shù)及本地變量的聲明語句中。
Java注解是附加在代碼中的一些元信息,用于一些工具在編譯、運行時進行解析和使用,起到說明、配置的功能。注解不會也不能影響代碼的實際邏輯,僅僅起到輔助性的作用。包含在 java.lang.annotation 包中。
你常見到的這個東東,還記得嗎?
@Override
public String toString() {
return "Test{}";
}
@Override 就是我們接下來要說的注解了,這個注解用來標記子類重寫的父類中的方法,如果父類中沒有該方法,則編譯器就會報錯提示開發(fā)者
常見的元注解
@Documented –注解是否將包含在JavaDoc中
@Retention –什么時候使用該注解
@Target? –注解用于什么地方
@Inherited – 是否允許子類繼承該注解
注解-簡而言之:解釋某個方法或者字段或者類,予以標記,讓代碼更易閱讀。
使用Annotation之前(甚至在使用之后),XML被廣泛的應用于描述元數(shù)據(jù)。不知何時開始一些應用開發(fā)人員和架構(gòu)師發(fā)現(xiàn)XML的維護越來越糟糕了。他們希望使用一些和代碼緊耦合的東西,而不是像XML那樣和代碼是松耦合的(在某些情況下甚至是完全分離的)代碼描述。
假如你想為應用設置很多的常量或參數(shù),這種情況下,XML是一個很好的選擇,因為它不會同特定的代碼相連。如果你想把某個方法聲明為服務,那么使用Annotation會更好一些,因為這種情況下需要注解和方法緊密耦合起來,開發(fā)人員也必須認識到這點
另一個很重要的因素是Annotation定義了一種標準的描述元數(shù)據(jù)的方式。在這之前,開發(fā)人員通常使用他們自己的方式定義元數(shù)據(jù)。例如,使用標記interfaces,注釋,transient關(guān)鍵字等等。每個程序員按照自己的方式定義元數(shù)據(jù),而不像Annotation這種標準的方式。
目前,許多框架將XML和Annotation兩種方式結(jié)合使用,平衡兩者之間的利弊。
簡單來說:本來可能需要很多配置文件,需要很多邏輯才能實現(xiàn)的內(nèi)容,就可以使用一個或者多個注解來替代,這樣就使得編程更加簡潔,代碼更加清晰。
注解的用處:
1、生成文檔。這是最常見的,也是java 最早提供的注解。常用的有@param @return 等
2、跟蹤代碼依賴性,實現(xiàn)替代配置文件功能。比如Dagger 2依賴注入,未來java開發(fā),將大量注解配置,具有很大用處;
3、在編譯時進行格式檢查。如@override 放在方法前,如果你這個方法并不是覆蓋了超類方法,則編譯時就能檢查出。
注解的原理:
注解本質(zhì)是一個繼承了Annotation的特殊接口,其具體實現(xiàn)類是Java運行時生成的動態(tài)代理類。而我們通過反射獲取注解時,返回的是Java運行時生成的動態(tài)代理對象$Proxy1。通過代理對象調(diào)用自定義注解(接口)的方法,會最終調(diào)用AnnotationInvocationHandler的invoke方法。該方法會從memberValues這個Map中索引出對應的值。而memberValues的來源是Java常量池。
簡單用下注解,隨機獻上老棧的DEMO
創(chuàng)建自定義注解類:
import com.sun.istack.internal.Interned;
import java.lang.annotation.*;
//該注解使用范圍在方法上
@Target(ElementType.METHOD)
//該注解用來指定編譯的時期
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
//配置幾個解釋的字段,如果該字段有默認值,可以使用default指定
public int id ();
public String description() default "test";
}
注解:根據(jù)需要設置字段,有時候也不需要設置,僅僅起到標記作用,類似于空接口
接下來:創(chuàng)建一個自定義的類
public class PasswordUtils {
@UseCase(id = 47, description ="密碼描述")
public boolean validatePassword(String password) {
return (password.matches("\\w*\\d\\w*"));
}
@UseCase(id = 50)
public String encryptPassword(String password) {
return new StringBuilder(password).reverse().toString();
}
}
這樣在測試類中,對于進行注解標記的字段,就可以通過java 反射,獲取到二進制文件,從而獲取在方法、字段、類、或者接口頂部的注解,而根據(jù)自定義注解的內(nèi)容,便可以根據(jù)數(shù)據(jù)的含義進行數(shù)據(jù)操作,簡化代碼,或者根據(jù)某個注解某個特殊的值來進行業(yè)務處理,很多框架也多有類似設計。
測試類:
public static void main(String[] args) {
List<Integer> useCases = new ArrayList<Integer>();
Collections.addAll(useCases, 47, 48, 49, 50);
trackUseCases(useCases, PasswordUtils.class);
}
public static void trackUseCases(List<Integer> useCases, Class<?> cl) {
for (Method m : cl.getDeclaredMethods()) {
UseCase uc = m.getAnnotation(UseCase.class);
if (uc != null) {
System.out.println("Found Use Case:" + uc.id() + " "
+ uc.description());
useCases.remove(new Integer(uc.id()));
}
}
for (int i : useCases) {
System.out.println("Warning: Missing use case-" + i);
}
}
注解學習要點:
1.注解的元注解。
2.注解的屬性。
3.注解主要給編譯器及工具類型的軟件用的。
4.注解的提取需要借助于 Java 的反射技術(shù),反射比較慢,所以注解使用時也需要謹慎計較時間成本。
待續(xù)。。。
追加一個補充例子: 對他人寫的代碼檢測,注解樣例:
注解樣例
package ceshi;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Jiecha {
}
*** 檢測類***
package ceshi;
import ceshi.Jiecha;
public class NoBug {
@Jiecha
public void suanShu(){
System.out.println("1234567890");
}
@Jiecha
public void jiafa(){
System.out.println("1+1="+1+1);
}
@Jiecha
public void jiefa(){
System.out.println("1-1="+(1-1));
}
@Jiecha
public void chengfa(){
System.out.println("3 x 5="+ 3*5);
}
@Jiecha
public void chufa(){
System.out.println("6 / 0="+ 6 / 0);
}
public void ziwojieshao(){
System.out.println("我寫的程序沒有 bug!");
}
}
檢測執(zhí)行:
package ceshi;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TestTool {
public static void main(String[] args) {
// TODO Auto-generated method stub
NoBug testobj = new NoBug();
Class clazz = testobj.getClass();
Method[] method = clazz.getDeclaredMethods();
//用來記錄測試產(chǎn)生的 log 信息
StringBuilder log = new StringBuilder();
// 記錄異常的次數(shù)
int errornum = 0;
for ( Method m: method ) {
// 只有被 @Jiecha 標注過的方法才進行測試
if ( m.isAnnotationPresent( Jiecha.class )) {
try {
m.setAccessible(true);
m.invoke(testobj, null);
} catch (Exception e) {
// TODO Auto-generated catch block
//e.printStackTrace();
errornum++;
log.append(m.getName());
log.append(" ");
log.append("has error:");
log.append("\n\r caused by ");
//記錄測試過程中,發(fā)生的異常的名稱
log.append(e.getCause().getClass().getSimpleName());
log.append("\n\r");
//記錄測試過程中,發(fā)生的異常的具體信息
log.append(e.getCause().getMessage());
log.append("\n\r");
}
}
}
log.append(clazz.getSimpleName());
log.append(" has ");
log.append(errornum);
log.append(" error.");
// 生成測試報告
System.out.println(log.toString());
}
}
結(jié)合反射, 注解效力。
如果大家有時間也可以研究下Hibernate的框架,通過注解是如何生成sql語句的呢? 日后有時間會更新該Demo,希望大家支持關(guān)注,過幾天手寫Hibernate注解Demo。。。