Java註解(Annotation)

註解經常與反射一塊兒使用。java

1、註解是什麼及做用

  註解(Annotation)至關於一種標記,在程序中加入註解就等於爲程序打上某種標記,沒有加,則等於沒有任何標記,之後,javac編譯器、開發工具和其餘程序能夠經過反射來了解你的類及各類元素上有無何種標記,看你的程序有什麼標記,就去幹相應的事,標記能夠加在包、類,屬性、方法,方法的參數以及局部變量上。程序員

  使用Annotation以前(甚至在使用以後),XML被普遍的應用於描述元數據。不知什麼時候開始一些應用開發人員和架構師發現XML的維護愈來愈糟糕了。他們但願使用一些和代碼緊耦合的東西,而不是像XML那樣和代碼是鬆耦合的(在某些狀況下甚至是徹底分離的)代碼描述。數組

   假如你想爲應用設置不少的常量或參數,這種狀況下,XML是一個很好的選擇,由於它不會同特定的代碼相連。若是你想把某個方法聲明爲服務,那麼使用Annotation會更好一些,由於這種狀況下須要註解和方法緊密耦合起來,開發人員也必須認識到這點。安全

  另外一個很重要的因素是Annotation定義了一種標準的描述元數據的方式。在這以前,開發人員一般使用他們本身的方式定義元數據。例如,使用標記interfaces,註釋,transient關鍵字等等。每一個程序員按照本身的方式定義元數據,而不像Annotation這種標準的方式。架構

  目前,許多框架將XML和Annotation兩種方式結合使用,平衡二者之間的利弊。
  事實上使用註解仍是XML的判斷標準應該是:該配置與代碼的相關度。若是代碼與配置相關度高,那麼使用註解配置,不然使用XML配置。框架

2、註解的分類

1. 按照運行機制劃分:源碼註解、編譯時註解、運行時註解ide

  • 源碼註解:只在源碼中存在,編譯成.class文件就不存在了
  • 編譯時註解:在源碼和.class文件中都存在,像@Override、@Deprecated、@SuppressWarnings,他們都屬於編譯時註解。
  • 運行時註解:在運行階段還起做用,甚至會影響運行邏輯的註解。像@Autowired自動注入的這樣一種註解就屬於運行時註解,它會在程序運行的時候把你的成員變量自動的注入進來。

2. 按照來源劃分:JDK的註解、第三方的註解、自定義註解函數

(1) JDK自帶的註解工具

Java提供了三種內建註解。開發工具

  • @Override:當咱們想要複寫父類中的方法時,咱們須要使用該註解去告知編譯器咱們想要複寫這個方法。這樣一來當父類中的方法移除或者發生更改時編譯器將提示錯
  • @Deprecated:當咱們但願編譯器知道某一方法不建議使用時,咱們應該使用這個註解。Java在javadoc 中推薦使用該註解,咱們應該提供爲何該方法不推薦使用以及替代的方法。
  • @SuppressWarnings:這個僅僅是告訴編譯器忽略特定的警告信息,例如在泛型中使用原生數據類型。它的保留策略是SOURCE(在源文件中有效)而且被編譯器丟棄。
  • @SafeVarargs:參數安全類型。JDK1.7引入的,主要目的是處理可變長參數中的泛型,此註解告訴編譯器:在可變長參數中的泛型是類型安全的。
  • @FunctionalInterface:函數式接口註解。JDK1.8引入的。

(2) 第三方註解

來自於各類框架 例如:@Service @Controller 等等

3. 元註解

元註解是給註解進行註解,能夠理解爲註解的註解就是元註解。

3、元註解

J2SE5.0版本在 java.lang.annotation提供了四種元註解,專門註解其餘的註解:

  • @Documented – 註解是否將包含在JavaDoc中
  • @Retention – 何時使用該註解
  • @Target – 註解用於什麼地方
  • @Inherited – 是否容許子類繼承該註解

1. @Retention註解

  @Retention定義註解的生命週期

  一個註解的生命週期有三個階段:java源文件是一個階段,class文件是一個階段,內存中的字節碼是一個階段,javac把java源文件編譯成.class文件時,有可能去掉裏面的註解,類加載器把.class文件加載到內存時也有可能去掉裏面的註解,所以在自定義註解時就可使用Retention註解指明自定義註解的生命週期,自定義註解的生命週期是在RetentionPolicy.SOURCE階段(java源文件階段),仍是在RetentionPolicy.CLASS階段(class文件階段),或者是在RetentionPolicy.RUNTIME階段(內存中的字節碼運行時階段),根據JDK提供的API能夠知道默認是在RetentionPolicy.CLASS階段 (JDK的API寫到:the retention policy defaults to RetentionPolicy.CLASS.)。

  @Retention(RetentionPolicy.SOURCE) //註解僅存在於源碼中,在class字節碼文件中不包含
  @Retention(RetentionPolicy.CLASS) // 默認的保留策略,註解會在class字節碼文件中存在,但運行時沒法得到,
  @Retention(RetentionPolicy.RUNTIME) // 註解會在class字節碼文件中存在,在運行時能夠經過反射獲取到

首先要明確生命週期長度 SOURCE < CLASS < RUNTIME ,因此前者能做用的地方後者必定也能做用。通常若是須要在運行時去動態獲取註解信息,那隻能用 RUNTIME 註解;若是要在編譯時進行一些預處理操做,好比生成一些輔助代碼(如 ButterKnife),就用 CLASS註解;若是隻是作一些檢查性的操做,好比 @Override 和 @SuppressWarnings,則可選用 SOURCE 註解。

2. @Target註解

@Target元註解決定了一個註解能夠標識到哪些成分上,如標識在在類身上,或者屬性身上,或者方法身上等成分,@Target默認值爲任何元素(成分),即定義註解的做用目標。

  • @Target(ElementType.TYPE) //接口、類、枚舉、註解
  • @Target(ElementType.FIELD) //字段、枚舉的常量
  • @Target(ElementType.METHOD) //方法
  • @Target(ElementType.PARAMETER) //方法參數
  • @Target(ElementType.CONSTRUCTOR) //構造函數
  • @Target(ElementType.LOCAL_VARIABLE)//局部變量
  • @Target(ElementType.ANNOTATION_TYPE)//註解
  • @Target(ElementType.PACKAGE) ///包

3. @Documented

@Documented:說明該註解將被包含在javadoc中。

4. @Inherited註解

Inherited是繼承的意思,可是並非說註解自己能夠繼承,而是說若是一個超類被@Inherited 註解過的註解進行註解的話,那麼若是它的子類沒有被任何註解應用的話,那麼這個子類就繼承了超類的註解。注意,僅針對類,成員屬性、方法並不受此註釋的影響。

4、自定義註解

1. 建立註解

@Retention(RetentionPolicy.RUNTIME) @Inherited @Target(ElementType.METHOD) public @interface MyAnnotation { }

2. 添加屬性

註解能夠當作是一種特殊的類,既然是類,天然能夠爲類添加屬性。

語法:類型 屬性名()

@Retention(RetentionPolicy.RUNTIME) @Inherited @Target(ElementType.METHOD) public @interface MyAnnotation { String color(); }

其實從代碼的寫法上來看,註解更像是一種特殊的接口,註解的屬性定義方式就和接口中定義方法的方式同樣,而應用了註解的類能夠認爲是實現了這個特殊的接口。

//屬性應用:
public class MyAnnotationTest { @MyAnnotation(color="red") public void testColor(){ System.out.println("紅色"); } public static void main(String[] args) { Method[] methods = MyAnnotationTest.class.getMethods(); for(Method method : methods){ if(method.isAnnotationPresent(MyAnnotation.class)){ MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class); System.out.println(myAnnotation.color()); } } } }

(1) 添加數組類型的屬性

增長數組類型的屬性:int[] arrayAttr() default {1,2,4};
應用數組類型的屬性:@MyAnnotation(arrayAttr={2,4,5})
若是數組屬性只有一個值,這時候屬性值部分能夠省略大括號,如:@MyAnnotation(arrayAttr=2),這就表示數組屬性只有一個值,值爲2。

(2) 添加枚舉類型的屬性

增長枚舉類型的屬性:EumTrafficLamp lamp() default EumTrafficLamp.RED;
應用枚舉類型的屬性:@MyAnnotation(lamp=EumTrafficLamp.GREEN)

3. 爲屬性指定默認值

語法:類型 屬性名() default 默認值

@Retention(RetentionPolicy.RUNTIME) @Inherited @Target({ElementType.METHOD,ElementType.TYPE}) public @interface MyAnnotation { String color() default "blue"; }

4. value屬性的說明

若是一個註解中有一個名稱爲value的屬性,且你只想設置value屬性(即其餘屬性都採用默認值或者你只有一個value屬性),那麼能夠省略掉「value=」部分。

@Retention(RetentionPolicy.RUNTIME) @Inherited @Target({ElementType.METHOD,ElementType.TYPE}) public @interface MyAnnotation { String color() default "blue"; String value(); //定義一個名稱爲value的屬性
}

屬性應用:

public class MyAnnotationTest { @MyAnnotation("測試value屬性") public void testColor(){ System.out.println("紅色"); } public void testValue(){ } public static void main(String[] args) { Method[] methods = MyAnnotationTest.class.getMethods(); for(Method method : methods){ if(method.isAnnotationPresent(MyAnnotation.class)){ MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class); System.out.println(myAnnotation.value()); } } } }

5、一些使用

1. 根據註解選擇實現類

① 公共的接口:OperationStrategy

public interface OperationStrategy { void deal(); }

② 每一個類都實現OperationStrategy並在類上添加註解@HanderType(自定義的註解), 建議放在同一個包下,便於搜索。

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Inherited public @interface HandlerType { int value(); //這裏固然也可使用枚舉
}

兩個接口的實現類:

@HandlerType(value = 2) public class AckStrategy implements OperationStrategy{ @Override public void deal() { System.out.println("======AckStrategy======="); } }
@HandlerType(value = 1) public class CancelStrategy implements OperationStrategy{ @Override public void deal() { System.out.println("======CancelStrategy======="); } }

③ 調用時,能夠先根據關鍵參數與@HanderType進行匹配,得到最終的接口實現類,建立實例對象,再調用具體的方法。

public class StrategyManager { public static OperationStrategy getStrategy(int flag) throws IllegalAccessException, InstantiationException { // 實例化Reflections,並指定要掃描的包名
        Reflections reflections = new Reflections(OperationStrategy.class.getPackage().getName()); // 獲取某個類的全部子類
        Set<Class<? extends OperationStrategy>> subTypes = reflections.getSubTypesOf(OperationStrategy.class); for(Class<?> clazz: subTypes){ HandlerType handlerType = clazz.getAnnotation(HandlerType.class); if(handlerType.value() == flag){ return ((OperationStrategy) clazz.newInstance()); } } return null; } }

④ 測試

public class DemoTest { public static void main(String[] args) throws InstantiationException, IllegalAccessException { OperationStrategy operationStrategy = StrategyManager.getStrategy(2); operationStrategy.deal(); } }

這裏用到了反射框架Reflections。

相關文章
相關標籤/搜索