從JDK1.5開始,Java就增長了Annotation這個新的功能,這種特性被稱爲元數據特性,同時也被稱爲註釋。html
系統內建的Annotation:java
提醒:如下這三個系統內建的Annotation位於java.lang包下linux
1.@Override,相信你們對這個比較熟悉,若是咱們要重寫一個類的方法的時候,要加上這個註解,可是不少人會反問,不加也是沒問題的,可是咱們必須考慮到的是程序的正確性,若是你自己的意圖是重寫這個方法,可是你在寫的時候把方法名寫錯了,那麼這就不是重寫了,也改變了意圖,因此在重寫的方法上加上這個註解是防患於未然,也是明確的告訴別人我這個方法是重寫的,如何我寫錯方法名,那麼這個註解也會提醒我。數組
2.@Deprecated,表示這個程序元素很危險或者存在更好的選擇,不鼓勵採用這種元素。ide
1 @Deprecated 2 public class DeprecatedDemo { 3 4 @Deprecated 5 public static final String name = "xujianguo"; 6 7 @Deprecated 8 public void print() { 9 System.out.println("This is the deprecated method"); 10 } 11 }
上面這個類說明了@Deprecated註解也是用在類上,成員屬性上,還有方法上,這個類用起來不會報錯,但會有警告的信息,表示你所用的東西已通過時了。工具
3.@SuppressWarnings,表示取消顯示指定的編譯器警告,經過這個註解咱們能夠取消一個沒必要要的警告,如泛型警告和過期警告,經過查看API文檔咱們能夠發現,該Annotation下有一個value的屬性,返回值一個String類型的數組,具體爲:public abstract String[] value,這個屬性實際上是一個警告集,裏面用放的是@SuppressWarnings能夠壓制的警告。spa
警告集:code
關鍵字 | 關鍵字 |
deprecation | 使用了不同意使用的類或者方法的警告 |
unchecked | 執行了未檢查的轉換警告,如泛型操做中沒有指定泛型 |
fallthrough | 當switch程序塊執行到下種狀況時沒有break語句的警告 |
path | 在類路徑、源文件路徑等中有着不存在路徑時的警告 |
serial | 當在可序列化類上缺乏serialVersionUID定義時的警告 |
finally | 任何finally子句不能完成時的警告 |
all | 關於以上全部的警告 |
下面演示一個壓制deprecatation和unchecked警告的Demo:htm
1 /** 2 * 該類實現了序列化接口,若無serialVersionUID會出現警告 3 * @author Guo 4 */ 5 @SuppressWarnings({"serial", "unchecked"}) 6 public class SuppressWarningsDemo implements Serializable{ 7 8 public static void main(String[] args) { 9 10 /** 11 * 沒有指定泛型,出現警告 12 * @author Guo 13 */ 14 List<String> list = new ArrayList(); 15 } 16 }
自定義Annotation:對象
定義本身的Annotation很是簡單,就像定義一個接口那樣:
1 [public] @interface MyAnnotation { 2 3 }
格式很簡單,一個Annotation可能接收各類參數,就像SuppressWarnings註解那樣,裏面能夠接收一個數組,下面咱們介紹一個它參數定義:
1.基本變量,能夠是String類型的,也能夠是int類型的,格式:public 類型 變量名();
2.數組類型,數組的定義格式也是大同小異:public 類型[] 變量名();
3.枚舉類型,經過定義枚舉類型,就能夠限定註解裏面的內容,格式:格式:public enum 變量名();
4.默認值,在Annotation中寫好默認值,在別的類上使用註解的時候就能夠不用寫值了,格式:public 類型 變量名() default 默認值;
下面簡單演示一下,你們加深一下印象:
1 enum MyEnum { 2 xp, win7, linux; 3 } 4 5 public @interface MyAnnotation { 6 7 public String name() default "xujianguo"; 8 public int age() default 20; 9 public String[] array(); 10 public MyEnum system() default MyEnum.linux; 11 } 12 13 class Test { 14 15 @MyAnnotation(name="zyp", age=20, array = {"zhou", "yan", "ping"}, system=MyEnum.win7) 16 public static void main(String[] args) { 17 System.out.println("Just Test"); 18 } 19 }
如今咱們要討論一個問題,你自定義好的就能在JVM跑嗎,其實你看看API中系統內建的三個Annotation,它們都使用了一個註解@Retention,這個註解位於java.lang.annotation包下,其實這個包下還有幾個Annotation,咱們也介紹一個,不太重點的是@Retention:
1.@Documented,指示某一類型的註釋將經過 javadoc 和相似的默認工具進行文檔化。應使用此類型來註釋這些類型的聲明:其註釋會影響由其客戶端註釋的元素的使用。若是類型聲明是用Documented 來註釋的,則其註釋將成爲註釋元素的公共 API 的一部分。簡單的說就是用了這個註解,你之後用該Annotation的時候在上面加上註釋會被記錄到文檔上。
2.@Inherited,指示註釋類型被自動繼承。若是在註釋類型聲明中存在 Inherited元註釋,而且用戶在某一類聲明中查詢該註釋類型,同時該類聲明中沒有此類型的註釋,則將在該類的超類中自動查詢該註釋類型。此過程會重複進行,直到找到此類型的註釋或到達了該類層次結構的頂層(Object) 爲止。若是沒有超類具備該類型的註釋,則查詢將指示當前類沒有這樣的註釋。簡單的說就是這個註解至關於extends啊,父類若是有了某個註解,那個這個註解在子類中也是擁有的,經過反射也能夠拿到註解上的信息。
3.@Target,指示註釋類型所適用的程序元素的種類。若是註釋類型聲明中不存在 Target 元註釋,則聲明的類型能夠用在任一程序元素上。若是存在這樣的元註釋,則編譯器強制實施指定的使用限制。簡單的說這個註解就是限制咱們的Annotation能夠用在什麼地方,它有個value屬性,是ElementType類型的,ElementType中規定如下幾種範圍:
範圍 |
描述 |
ANNOTATION_TYPE |
只能用在註釋聲明上 |
CONSTRUCTOR |
只能用在構造方法上 |
FIELD |
只能用在字段的聲明上 |
LOCAL_VARIABLE |
只能用在局部變量的聲明上 |
METHOD |
只能用在方法的聲明上 |
PACKAGE |
只能用在包的聲明上 |
PARAMETER |
只能用在參數的聲明上 |
TYPE |
只能用在類、接口、枚舉類型上 |
提醒:其實這些限制的範圍是能夠疊加的,例如,你的Annotation想在類或者方法上使用,能夠這麼寫:@Target(ElementType.TYPE, ElementType.METHOD)
4.@Retention,指示註釋類型的註釋要保留多久。這個註釋有個value的屬性,屬性的類型爲RetentionPolicy,而RetentionPolicy裏面有三個常變量,咱們一塊兒來看看這三個常變量。
範圍 | 描述 |
SOURCE | 此Annotation的信息只會保存在程序源文件中(java文件),不會保留在編譯好的文件中(class文件) |
CLASS | 此Annotation的信息保留在程序源文件(java文件)和編譯好的文件中(class文件),使用此類的時候 Annotation的信息不會被加載到JVM中,若是一個Annotation沒有聲明使用什麼範圍,這個就是默認範圍。 |
RUNTIME | 此Annotation的信息會保留在源文件、類文件中,還會被加載到JVM中 |
很明確的看出RUNTIME纔是咱們想要的菜,由於咱們要利用Annotation去獲取一些信息,咱們也來看看咱們系統內建的Annotation會屬於哪些呢?@Override採用的是Retention(value=RetentionPolicy.SOURCE),@Deprecated採用的是Retention(value=RetentionPolicy.RUNTIME),@SuppressWarnings採用的也是Retention(value=RetentionPolicy.SOURCE),總結一下,一個能真正對咱們來講起做用的Annotation應該這樣定義:
1 @Retention(value=RetentionPolicy.RUNTIME) 2 public @interface MyAnnotation { 3 4 public String name() default "xujianguo"; 5 public int age() default 20; 6 public String[] array(); 7 public MyEnum system() default MyEnum.linux; 8 }
Annotation與反射:
說到Annotation的應用,則不會離開反射,在Class類中存在如下幾種跟Annotation操做相關的方法:
方法 | 描述 |
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) | 若是存在該元素的指定類型的註釋,則返回這些註釋,不然返回 null |
public Annotation[] getAnnotations() |
返回此元素上存在的全部註釋 |
public Annotation[] getDeclaredAnnotations() | 返回直接存在於此元素上的全部註釋。 |
public boolean isAnnotation() | 判斷元素是否表示一個註釋 |
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) | 若是指定類型的註釋存在於此元素上,則返回 true,不然返回 false |
下面我自定義一個Annotation,用這個Annotation來模仿JUnit的@Test註解,同時自定義的這個Annotation也有屬性,要將這個屬性的值拿出來:
自定義的Annotation-TestSimulation:
1 @Retention(value=RetentionPolicy.RUNTIME) 2 public @interface TestSimulation { 3 public String author() default "xujianguo"; 4 }
運用註解的類AnnotationDemo類:
1 public class AnnotationDemo { 2 3 @TestSimulation(author="zhouyanping") 4 public void print() { 5 System.out.println("This is the method of print"); 6 } 7 8 public void say() { 9 System.out.println("This is the method of say"); 10 } 11 12 @TestSimulation 13 public void coding() { 14 System.out.println("This is the method of coding"); 15 } 16 }
進行反射解析的AnnotationUtil類:
1 public class AnnotationUtil { 2 3 public static void main(String[] args) throws Exception { 4 5 /** 6 * 反射類的對象和拿出一個方法組 7 * @author Guo 8 */ 9 Class clazz = Class.forName("com.xujianguo.test.AnnotationDemo"); 10 Object object = clazz.newInstance(); 11 Method[] methods = clazz.getMethods(); 12 13 for(Method method : methods) { 14 15 /** 16 * 對方法上的註解進行覈對 17 * @author Guo 18 */ 19 if(method.isAnnotationPresent(TestSimulation.class)) { 20 21 /** 22 * 拿到指定的Annotation並獲取相應的信息 23 * @author Guo 24 */ 25 TestSimulation ts = method.getAnnotation(TestSimulation.class); 26 System.out.println(ts.author()); 27 method.invoke(object); 28 } 29 } 30 } 31 }