從JDK5.0開始,Java增長對元數據(MetaData)的支持,也就是註解(Annotation)。其實咱們早就已經接觸過註解了,例如咱們常常在Java代碼中能夠看到 「@Override」,「@Test」等等這樣的東西,它們就是Java中的註解。註解能夠像修飾符同樣使用,能夠用於修飾包、類、構造器、方法、成員變量、參數、局部變量的聲明。html
咱們須要注意的是,註解與註釋是有必定區別的,註解就是代碼裏面的特殊標記,這些標記能夠在編譯,類加載,運行時被讀取,並執行相應的處理。經過註解開發人員能夠在不改變原有代碼和邏輯的狀況下在源代碼中嵌入補充信息。而註釋則是用以說明某段代碼的做用,或者說明某個類的用途、某個方法的功能和介紹,以及該方法的參數和返回值的數據類型及意義等等。java
在JavaSE部分,註解的使用每每比較簡單,Java中提供了5個內置註解,它們分別是:數組
①、@Override:標註該方法是重寫父類中的方法。安全
這個註解一個是咱們見得最多的一個了,提示這個方法是重寫於父類的方法。ide
②、@Deprecated:標記某個功能已通過時,用於定義過期的類、方法、成員變量等。函數
這個註解想必你們應該都有碰到過,在使用Date日期類的時候,裏面有大量過期的方法,咱們來定義一個Date類來調用一個方法。工具
這個getDay()方法就是過期的,咱們點擊進去看一下這個方法的源碼:學習
果真這個方法是用@Deprecated修飾過的。同時也能夠發現咱們在調用過期元素時,編譯器在編譯階段遇到這個註解時會發出提醒警告,告訴開發者正在調用一個過期的元素,固然若是不想看到警告咱們能夠抑制它的出現。測試
③、@SuppressWarnings:抑制編譯器警告。spa
上面說到用@Deprecated修飾過的元素在調用時會有警告,咱們能夠用@SuppressWarnings註解來抑制警告的出現。
能夠發現左邊的警告沒有了。@SuppressWarnings這個註解中參數很是的多,這裏介紹幾個常見的參數:
若是須要了解更多的能夠去查看官方文檔。
④、@FunctionaInterface:指定接口必須爲函數式接口。
這個註解是Java8出現的新特性。這個函數式接口的意思就是接口中有一個且僅有一個抽象方法,可是能夠有多個非抽象方法,若是不定義或定義多個抽象方法就會報錯。
正式由於JDK 8中lambda表達式的引入,使得函數式接口在Java中變得愈來愈流行。由於這些特殊類型的接口能夠用lambda表達式、方法引用或構造函數引用輕鬆替換。
⑤、@SafeVarargs:抑制"堆污染警告"。
這個註解是在Java7中引入,主要目的是處理可變長參數中的泛型,此註解告訴編譯器:在可變長參數中的泛型是類型安全的。可變長參數是使用數組存儲的,而數組和泛型不能很好的混合使用。由於數組元素的數據類型在編譯和運行時都是肯定的,而泛型的數據類型只有在運行時才能肯定下來,所以當把一個泛型存儲到數組中時,編譯器在編譯階段沒法檢查數據類型是否匹配,所以會給出警告信息。
咱們來看下面這個示例:
public class Test { @SafeVarargs//這裏告訴編譯器類型安全,不讓有警告。其實方法體內容類型不安全 public static void show(List<String>...lists){ Object[] arry=lists; List<Integer> intList=Arrays.asList(11,22,33); arry[0]=intList;//這裏就是堆污染,這裏沒有警告,是由於只針對於可變長參數泛型 String str=lists[0].get(0);//java.lang.ClassCastException } public static void main(String[] args) { List<String> list1=Arrays.asList("AA","BB","CC"); List<String> list2=Arrays.asList("DD","EE","DD"); show(list1,list2); } }
經過上述的示例,咱們將intList賦給array[0],array[0]的類型是List<String>,可是儲引用到實際爲List<Integer>類型的值,這個無效的引用被稱爲堆污染。因爲直到運行時才能肯定此錯誤,所以它會在編譯時顯示爲警告,這裏沒有警告,是由於只針對於可變長參數泛型,並在運行時出現ClassCastException。
注意:@SafeVarargs註解只能用在參數長度可變的方法或構造方法上,且方法必須聲明爲static或final,不然會出現編譯錯誤。
咱們在享受註解給咱們帶來方便地同時,咱們本身應該要知道怎麼去定義註解。註解的自定義很是的簡單,經過 @interface關鍵字進行定義,能夠發現這個關鍵字和接口interface很類似,就在前面加了一個 @符號,可是它和接口沒有任何關係。自定義註解還須要注意的一點是:全部的自定義註解都自動繼承了java.lang.annotation.Annotation這個接口。自定義註解的格式:
public @interface 註解名 { //屬性 }
一樣咱們能夠在註解中定義屬性,它的定義有點相似於方法,但又不是方法,在註解中是不能聲明普通方法的。註解的屬性在註解定義中以無參數方法的形式來聲明,其方法名定義了屬性的名字,其返回值定義了該屬性的類型,咱們稱爲配置參數。它們的類型只能是八種基本數據類型、String類型、Class類型、enum類型、Annotation類型以上全部類型的數組。例如:
//定義了一個MyAnnotation註解 public @interface MyAnnotation { String[] value(); } @MyAnnotation(value = "hello") class Test{ }
上面註解代碼中,定義了一個String的value數組。而後咱們在使用的時候,就可使用 屬性名稱=「xxx」 的形式賦值。
註解中屬性還能夠有默認值,默認值須要用 default 關鍵值指定。好比:.
//定義了一個MyAnnotation註解 public @interface MyAnnotation { String id(); String[] value() default {"AA","BB"}; } @MyAnnotation(id="one") class Test{ }
上面定義了 id 屬性沒有默認值,而value屬性中則設置了默認值,因此在使用註解的時候只需給 id 屬性賦值便可,value能夠不用寫。
經過以上形式自定義的註解暫時都尚未任何實用的價值,由於自定義註解必須配上註解的信息處理流程(使用反射)纔有意義。如何讓註解真真的發揮做用,主要就在於註解處理方法,因此接下來咱們將學習元註解和註解的反射。
元註解就是用來修飾其餘註解的註解。咱們隨便點進一個註解的源碼均可以發現有元註解。
Java5.0中定義了4個標準的元註解類型,它們被用來提供對其它註解類型做說明:
而Java8.0中又增長了一個新的元註解類型:
因此接下來咱們將逐個分析它們的做用和使用方法。
一、@Retention:用於指定該Annotation的生命週期。
這個元註解只能用於修飾一個Annotation定義,它的內部包含了一個RetentionPolicy枚舉類型的屬性,而這個枚舉類中定義了三個枚舉實例,SOURCE、CLASS、RUNTIME。它們各個值的意思以下:
比較典型的是@SuppressWarnings註解,若是咱們用 javap -c去反編譯它是看到這個註解的,由於在編譯的時候就已經被丟棄了。
②、@Target:用於指定該Annotation可以用在哪些地方。
@Target內部定義了一個枚舉類型的數組ElementType[] value(),在ElementType這個枚舉類中參數有不少,咱們來看一下:
③、@Document:表示Annotation能夠被包含到javadoc中去。默認狀況下javadoc是不包含註解的。
因爲這個比較簡單因此不細說。
④、@Inherited:被它修飾的Annotation將具備繼承性。
@Inherited修飾過的Annotation其子類會自動具備該註解。在實際應用中,使用狀況很是少。
⑤、@Repeatable:用於指示它修飾的註解類型是可重複的。
這個註解是在Java8中新出的特性,說到這個可重複註解可能有點不理解。咱們經過示例來理解一下:
先定義一個MyAnnotation註解:
@Inherited @Documented @Repeatable(MyAnnotations.class) @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD,ElementType.PARAMETER}) public @interface MyAnnotation { String value() default "Hello"; }
這裏須要說明@Repeatable(MyAnnotations.class),它表示在同一個類中@MyAnnotation註解是能夠重複使用的,重複的註解被存放至@MyAnnotations註解中。
而後再定義一個MyAnnotations註解:
@Inherited @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD,ElementType.PARAMETER}) public @interface MyAnnotations { MyAnnotation[] value(); }
這個MyAnnotations註解裏面的屬性必需要聲明爲要重複使用註解的類型數組MyAnnotation[] value();,並且相應的生命週期和使用地方都須要同步。不然就會編譯報錯!
進行測試:
@MyAnnotation(value = "World") @MyAnnotation(value = "World") public class Test{ }
而在Java8以前沒有@Repeatable註解是這樣寫的:
@MyAnnotations({@MyAnnotation(value = "World"),@MyAnnotation(value = "World")}) public class Test{ }
以上講的因此註解的定義都只是一個形式,實際上還並無什麼可以使用的價值,而註解的核心就是使用反射來獲取到它們,因此下面咱們要來學習註解處理器(使用反射)的使用。
下面參考:https://www.cnblogs.com/peida/archive/2013/04/26/3038503.html
java.lang.reflect 包下主要包含一些實現反射功能的工具類,實際上,java.lang.reflect 包全部提供的反射API擴充了讀取運行時Annotation信息的能力。當一個Annotation類型被定義爲RUNTIME的註解後,該註解才能是運行時可見,當class文件被裝載時被保存在class文件中的Annotation纔會被虛擬機讀取。
AnnotatedElement 接口是全部程序元素(Class、Method和Constructor)的父接口,因此程序經過反射獲取了某個類的AnnotatedElement對象以後,程序就能夠調用該對象的以下四個個方法來訪問Annotation信息:
一個簡單的註解處理器:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME)//這裏必須定義爲RUNTIME public @interface MyAnnotation { String id(); String[] value() default {"AA","BB"}; } @MyAnnotation(id = "hello") class Test{ public static void main(String[] args) { boolean annotationPresent = Test.class.isAnnotationPresent(MyAnnotation.class); System.out.println(annotationPresent); if ( annotationPresent ) { MyAnnotation myAnnotation = Test.class.getAnnotation(MyAnnotation.class); System.out.println("id:"+myAnnotation.id()); System.out.println("value:"+ Arrays.toString(myAnnotation.value())); } } }
程序運行結果:
上面的例子只是做用在類上面的註解,若是要做用在屬性、方法等上面的註解咱們應該怎麼獲取呢?
定義做用於類上面的MyAnnotation註解:
//做用於類上面的註解 @Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME)//這裏必須定義爲RUNTIME public @interface MyAnnotation { String[] value() default ""; }
定義做用於屬性上面的AttributeAnnotation註解:
//做用於屬性上的註解 @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME)//這裏必須定義爲RUNTIME public @interface AttributeAnnotation { String value(); }
定義做用於方法上面的MethodAnnotation註解:
//做用於方法上的註解 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME)//這裏必須定義爲RUNTIME public @interface MethodAnnotation { String value(); }
而後就是測試了:
@MyAnnotation(value = "MyAnnotation") class Test{ @AttributeAnnotation(value = "AttributeAnnotation") String str; @MethodAnnotation(value = "MethodAnnotation_show") public void show(){ System.out.println("MethodAnnotation_show"); } @MethodAnnotation(value = "MethodAnnotation_display") public void display(){ System.out.println("MethodAnnotation_display"); } public static void main(String[] args) { //獲取類上面的註解 boolean annotationPresent = Test.class.isAnnotationPresent(MyAnnotation.class); System.out.println(annotationPresent); if ( annotationPresent ) { MyAnnotation myAnnotation = Test.class.getAnnotation(MyAnnotation.class); System.out.println("class-annotation:"+Arrays.toString(myAnnotation.value())); } try { //獲取單個屬性中的註解 Field str = Test.class.getDeclaredField("str"); AttributeAnnotation attributeAnnotation = str.getAnnotation(AttributeAnnotation.class); if (attributeAnnotation!=null){ System.out.println("attribute-annotation:"+attributeAnnotation.value()); } //獲取多個方法中的註解 Method[] declaredMethods = Test.class.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { MethodAnnotation methodAnnotation = declaredMethod.getAnnotation(MethodAnnotation.class); if (methodAnnotation!=null){ System.out.println("method-annotation:"+methodAnnotation.value()); } } } catch (NoSuchFieldException e) { e.printStackTrace(); } } }
程序運行結果:
小弟菜鳥只能領悟這麼多了,若是有錯誤或者須要補充的地方歡迎你們留言指出。謝謝!!!