注解(Annotation)很重要,未來的開發模式都是基于注解的,JPA是基于注解的,Spring2.5以上都是基于注解的,Hibernate3.x以后也是基于注解的,現在的Struts2有一部分也是基于注解的了,注解是一種趨勢,現在已經有不少的人開始用注解了,注解是JDK1.5之后才有的新特性
JDK1.5之后內部提供的三個注解
@Deprecated 意思是“廢棄的,過時的”
@Override 意思是“重寫、覆蓋”
@SuppressWarnings 意思是“壓縮警告”
范例:注解的應用:
1 package cn.gacl.annotation; 2 /** 3 * 此類是用來演示注解(Annotation)的應用的,注解也是JDK1.5新增加的特性之一 4 * JDK1.5內部提供的三種注解是:@SuppressWarnings(":deprecation")、@Deprecated、@Override 5 * @author 孤傲蒼狼 6 * 7 */ 8 /** 9 * 類名的命名是有講究的,類名、屬性名、變量名一般是名詞,或者是形容詞+名詞,方法一般是動詞,或者是動詞+名詞, 10 * 以AnnotationTest作為類名和以TestAnnotation作為類名是有區別的, 11 * 前者是注解的測試,符合名詞的特征,后者是測試注解,聽起來就是一個動作名稱,是方法的命名特征 12 */ 13 public class AnnotationTest { 14 /** 15 * @param args 16 */ 17 @SuppressWarnings(":deprecation") 18 //這里就是注解,稱為壓縮警告,這是JDK內部自帶的一個注解,一個注解就是一個類,在這里使用了這個注解就是創建了SuppressWarnings類的一個實例對象 19 public static void main(String[] args) { 20 System.runFinalizersOnExit(true); 21 //The method runFinalizersOnExit(boolean) from the type System is deprecated(過時的,廢棄的) 22 //這里的runFinalizersOnExit()方法畫了一條橫線表示此方法已經過時了,不建議使用了 23 } 24 @Deprecated //這也是JDK內部自帶的一個注解,意思就是說這個方法已經廢棄了,不建議使用了 25 public static void sayHello(){ 26 System.out.println("hi,孤傲蒼狼"); 27 } 28 @Override //這也是JDK1.5之后內部提供的一個注解,意思就是要重寫(覆蓋)JDK內部的toString()方法 29 public String toString(){ 30 return "孤傲蒼狼"; 31 } 32 }
總結:注解(Annotation)相當于一種標記,在程序中加入注解就等于為程序打上某種標記,沒有加,則等于沒有任何標記,以后,javac編譯器、開發工具和其他程序可以通過反射來了解你的類及各種元素上有無何種標記,看你的程序有什么標記,就去干相應的事,標記可以加在包、類,屬性、方法,方法的參數以及局部變量上。
注解就相當于一個你的源程序要調用一個類,在源程序中應用某個注解,得事先準備好這個注解類。就像你要調用某個類,得事先開發好這個類。
自定義一個最簡單的注解:
1 public @interface MyAnnotation{}
1 package cn.gacl.annotation; 2 import java.lang.annotation.ElementType; 3 import java.lang.annotation.Retention; 4 import java.lang.annotation.RetentionPolicy; 5 import java.lang.annotation.Target; 6 /** 7 * 這是一個自定義的注解(Annotation)類 在定義注解(Annotation)類時使用了另一個注解類Retention 8 * 在注解類上使用另一個注解類,那么被使用的注解類就稱為元注解 9 * 10 * @author 孤傲蒼狼 11 * 12 */ 13 @Retention(RetentionPolicy.RUNTIME) 14 //Retention注解決定MyAnnotation注解的生命周期 15 @Target( { ElementType.METHOD, ElementType.TYPE }) 16 //Target注解決定MyAnnotation注解可以加在哪些成分上,如加在類身上,或者屬性身上,或者方法身上等成分 17 /* 18 * @Retention(RetentionPolicy.SOURCE) 19 * 這個注解的意思是讓MyAnnotation注解只在java源文件中存在,編譯成.class文件后注解就不存在了 20 * @Retention(RetentionPolicy.CLASS) 21 * 這個注解的意思是讓MyAnnotation注解在java源文件(.java文件)中存在,編譯成.class文件后注解也還存在, 22 * 被MyAnnotation注解類標識的類被類加載器加載到內存中后MyAnnotation注解就不存在了 23 */ 24 /* 25 * 這里是在注解類MyAnnotation上使用另一個注解類,這里的Retention稱為元注解。 26 * Retention注解括號中的"RetentionPolicy.RUNTIME"意思是讓MyAnnotation這個注解的生命周期一直程序運行時都存在 27 */ 28 public @interface MyAnnotation { 29 }
把自定義的注解加到某個類上:
1 @ MyAnnotation 2 public class AnnotationUse{ 3 4 }
用反射測試進行測試AnnotationUse的定義上是否有@MyAnnotation
1 package cn.gacl.annotation; 2 @MyAnnotation 3 //這里是將新創建好的注解類MyAnnotation標記到AnnotaionTest類上 4 public class AnnotationUse { 5 public static void main(String[] args) { 6 // 這里是檢查Annotation類是否有注解,這里需要使用反射才能完成對Annotation類的檢查 7 if (AnnotationUse.class.isAnnotationPresent(MyAnnotation.class)) { 8 /* 9 * MyAnnotation是一個類,這個類的實例對象annotation是通過反射得到的,這個實例對象是如何創建的呢? 10 * 一旦在某個類上使用了@MyAnnotation,那么這個MyAnnotation類的實例對象annotation就會被創建出來了 11 * 假設很多人考駕照,教練在有些學員身上貼一些綠牌子、黃牌子,貼綠牌子的表示送禮送得比較多的, 12 * 貼黃牌子的學員表示送禮送得比較少的,不貼牌子的學員表示沒有送過禮的,通過這個牌子就可以標識出不同的學員 13 * 教官在考核時一看,哦,這個學員是有牌子的,是送過禮給他的,優先讓有牌子的學員過,此時這個牌子就是一個注解 14 * 一個牌子就是一個注解的實例對象,實實在在存在的牌子就是一個實實在在的注解對象,把牌子拿下來(去掉注解)注解對象就不存在了 15 */ 16 MyAnnotation annotation = (MyAnnotation) AnnotationUse.class 17 .getAnnotation(MyAnnotation.class); 18 System.out.println(annotation);// 打印MyAnnotation對象,這里輸出的結果為:@cn.itcast.day2.MyAnnotation() 19 } 20 } 21 }
根據反射的測試的問題,引出@Retention元注解的講解:其三種取值:RetentionPolicy.SOURCE、RetentionPolicy.CLASS、RetentionPolicy.RUNTIME分別對應:Java源文件(.java文件)—->.class文件—->內存中的字節碼
當在Java源程序上加了一個注解,這個Java源程序要由javac去編譯,javac把java源文件編譯成.class文件,在編譯成class時可能會把Java源程序上的一些注解給去掉,java編譯器(javac)在處理java源程序時,可能會認為這個注解沒有用了,于是就把這個注解去掉了,那么此時在編譯好的class中就找不到注解了, 這是編譯器編譯java源程序時對注解進行處理的第一種可能情況,假設java編譯器在把java源程序編譯成class時,沒有把java源程序中的注解去掉,那么此時在編譯好的class中就可以找到注解,當程序使用編譯好的class文件時,需要用類加載器把class文件加載到內存中,class文件中的東西不是字節碼,class文件里面的東西由類加載器加載到內存中去,類加載器在加載class文件時,會對class文件里面的東西進行處理,如安全檢查,處理完以后得到的最終在內存中的二進制的東西才是字節碼,類加載器在把class文件加載到內存中時也有轉換,轉換時是否把class文件中的注解保留下來,這也有說法,所以說一個注解的生命周期有三個階段: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.)
下面看看@Deprecated、@Override、@SuppressWarnings這三個注解的@Retention注解的屬性值分別是什么吧
Java API中是這樣定義的@Deprecated的
1 @Documented 2 @Retention(value=RUNTIME) 3 public @interface Deprecated
Java API中是這樣定義的@Override的
1 @Target(value=METHOD) 2 @Retention(value=SOURCE) 3 public @interface Override
@Override是給javac(java編譯器)看的,編譯完以后就@Override注解就沒有價值了,@Override注解在源代碼中有用,編譯成.class文件后@Override注解就沒有用了,因此@Override的Retention的屬性值是RetentionPolicy.SOURCE
Java API中是這樣定義的@SuppressWarnings的
1 @Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE}) 2 @Retention(value=SOURCE) 3 public @interface SuppressWarnings
@SuppressWarnings是給javac(java編譯器)看的,編譯器編譯完java文件后,@SuppressWarnings注解就沒有用了,所以@SuppressWarnings的Retention的屬性值是RetentionPolicy.SOURCE
@Target元注解決定了一個注解可以標識到哪些成分上,如標識在在類身上,或者屬性身上,或者方法身上等成分,@Target默認值為任何元素(成分)
例如:
1 @Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE}) 2 @Retention(value=SOURCE) 3 public @interface SuppressWarnings
注解可以看成是一種特殊的類,既然是類,那自然可以為類添加屬性
語法:類型 屬性名();
1 package cn.gacl.annotation; 2 3 import java.lang.annotation.ElementType; 4 import java.lang.annotation.Retention; 5 import java.lang.annotation.RetentionPolicy; 6 import java.lang.annotation.Target; 7 8 @Retention(RetentionPolicy.RUNTIME) 9 //Retention注解決定MyAnnotation注解的生命周期 10 @Target( { ElementType.METHOD, ElementType.TYPE }) 11 public @interface MyAnnotation { 12 /** 13 * 定義基本屬性 14 * @return 15 */ 16 String color(); 17 }
其實從代碼的寫法上來看,注解更像是一種特殊的接口,注解的屬性定義方式就和接口中定義方法的方式一樣,而應用了注解的類可以認為是實現了這個特殊的接口
1 package cn.gacl.annotation; 2 3 @MyAnnotation(color="red")//應用MyAnnotation注解的color屬性 4 public class MyAnnotationTest { 5 public static void main(String[] args) { 6 /** 7 * 用反射方式獲得注解對應的實例對象后,在通過該對象調用屬性對應的方法 8 */ 9 MyAnnotation annotation = (MyAnnotation) MyAnnotationTest.class.getAnnotation(MyAnnotation.class); 10 System.out.println(annotation.color());//輸出red 11 } 12 }
語法:類型 屬性名() default 默認值;
package cn.gacl.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) //Retention注解決定MyAnnotation注解的生命周期 @Target( { ElementType.METHOD, ElementType.TYPE }) public @interface MyAnnotation { String color() default "blue";//為屬性指定缺省值 }
1 package cn.gacl.annotation; 2 3 @MyAnnotation 4 public class MyAnnotationTest { 5 public static void main(String[] args) { 6 /** 7 * 用反射方式獲得注解對應的實例對象后,在通過該對象調用屬性對應的方法 8 */ 9 MyAnnotation annotation = (MyAnnotation) MyAnnotationTest.class.getAnnotation(MyAnnotation.class); 10 System.out.println(annotation.color());//輸出color屬性的默認值:blue 11 12 } 13 }
如果一個注解中有一個名稱為value的屬性,且你只想設置value屬性(即其他屬性都采用默認值或者你只有一個value屬性),那么可以省略掉“value=”部分。
例如:@SuppressWarnings(“deprecation”)
1 package cn.gacl.annotation; 2 3 import java.lang.annotation.ElementType; 4 import java.lang.annotation.Retention; 5 import java.lang.annotation.RetentionPolicy; 6 import java.lang.annotation.Target; 7 8 @Retention(RetentionPolicy.RUNTIME) 9 //Retention注解決定MyAnnotation注解的生命周期 10 @Target( { ElementType.METHOD, ElementType.TYPE }) 11 public @interface MyAnnotation { 12 String color() default "blue";//為屬性指定缺省值 13 String value();//定義一個名稱為value的屬性 14 }
1 package cn.gacl.annotation; 2 3 @MyAnnotation("孤傲蒼狼")//等價于@MyAnnotation(value="孤傲蒼狼") 4 public class MyAnnotationTest { 5 public static void main(String[] args) { 6 /** 7 * 用反射方式獲得注解對應的實例對象后,在通過該對象調用屬性對應的方法 8 */ 9 MyAnnotation annotation = (MyAnnotation) MyAnnotationTest.class.getAnnotation(MyAnnotation.class); 10 System.out.println(annotation.color());//輸出color屬性的默認值:blue 11 System.out.println(annotation.value()); 12 13 } 14 }
1 /** 2 * MetaAnnotation注解類為元注解 3 * @author 孤傲蒼狼 4 * 5 */ 6 public @interface MetaAnnotation { 7 String value();//元注解MetaAnnotation設置有一個唯一的屬性value 8 }
為注解添加一個注解類型的屬性,并指定注解屬性的缺省值:MetaAnnotation annotationAttr() default @MetaAnnotation(“xdp”);
EumTrafficLamp.java
1 package cn.gacl.annotation; 2 /** 3 * 交通信號燈顏色枚舉 4 * @author 孤傲蒼狼 5 * 6 */ 7 public enum EumTrafficLamp { 8 RED,//紅 9 YELLOW,//黃 10 GREEN//綠 11 }
MetaAnnotation.java
1 /** 2 * MetaAnnotation注解類為元注解 3 * @author 孤傲蒼狼 4 * 5 */ 6 public @interface MetaAnnotation { 7 String value();//元注解MetaAnnotation設置有一個唯一的屬性value 8 }
MyAnnotation.java
1 package cn.gacl.annotation; 2 3 import java.lang.annotation.ElementType; 4 import java.lang.annotation.Retention; 5 import java.lang.annotation.RetentionPolicy; 6 import java.lang.annotation.Target; 7 8 @Retention(RetentionPolicy.RUNTIME) 9 //Retention注解決定MyAnnotation注解的生命周期 10 @Target({ElementType.METHOD, ElementType.TYPE}) 11 public @interface MyAnnotation { 12 String color() default "blue";//為屬性指定缺省值 13 /** 14 * 為注解添加value屬性,這個value屬性很特殊,如果一個注解中只有一個value屬性要設置, 15 * 那么在設置注解的屬性值時,可以省略屬性名和等號不寫, 直接寫屬性值,如@SuppressWarnings("deprecation"), 16 * 這里的MyAnnotation注解設置了兩個String類型的屬性,color和value, 17 * 因為color屬性指定有缺省值,value屬性又是屬于特殊的屬性,因此使用MyAnnotation注解時 18 * 可以這樣使用MyAnnotation注解:"@MyAnnotation(color="red",value="xdp")" 19 * 也可以這樣使用:"@MyAnnotation("孤傲蒼狼")",這樣寫就表示MyAnnotation注解只有一個value屬性要設置,color屬性采用缺省值 20 * 當一個注解只有一個value屬性要設置時,是可以省略"value="的 21 */ 22 String value();//定義一個名稱為value的屬性 23 //添加一個int類型數組的屬性 24 int[] arrayAttr() default {1,2,4}; 25 //添加一個枚舉類型的屬性,并指定枚舉屬性的缺省值,缺省值只能從枚舉類EumTrafficLamp中定義的枚舉對象中取出任意一個作為缺省值 26 EumTrafficLamp lamp() default EumTrafficLamp.RED; 27 //為注解添加一個注解類型的屬性,并指定注解屬性的缺省值 28 MetaAnnotation annotationAttr() default @MetaAnnotation("xdp"); 29 30 }
MyAnnotationTest.java
1 package cn.gacl.annotation; 2 /** 3 * 這里是將新創建好的注解類MyAnnotation標記到AnnotaionTest類上, 4 * 并應用了注解類MyAnnotation中定義各種不同類型的的屬性 5 */ 6 @MyAnnotation( 7 color="red", 8 value="孤傲蒼狼", 9 arrayAttr={3,5,6}, 10 lamp=EumTrafficLamp.GREEN, 11 annotationAttr=@MetaAnnotation("gacl") 12 ) 13 public class MyAnnotationTest { 14 @MyAnnotation("將MyAnnotation注解標注到main方法上") 15 public static void main(String[] args) { 16 /** 17 * 這里是檢查Annotation類是否有注解,這里需要使用反射才能完成對Annotation類的檢查 18 */ 19 if(MyAnnotationTest.class.isAnnotationPresent(MyAnnotation.class)) { 20 /** 21 * 用反射方式獲得注解對應的實例對象后,在通過該對象調用屬性對應的方法 22 * MyAnnotation是一個類,這個類的實例對象annotation是通過反射得到的,這個實例對象是如何創建的呢? 23 * 一旦在某個類上使用了@MyAnnotation,那么這個MyAnnotation類的實例對象annotation就會被創建出來了 24 */ 25 MyAnnotation annotation = (MyAnnotation) MyAnnotationTest.class.getAnnotation(MyAnnotation.class); 26 System.out.println(annotation.color());//輸出color屬性的默認值:red 27 System.out.println(annotation.value());//輸出value屬性的默認值:孤傲蒼狼 28 System.out.println(annotation.arrayAttr().length);//這里輸出的數組屬性的長度的結果為:3,數組屬性有三個元素,因此數組的長度為3 29 System.out.println(annotation.lamp());//這里輸出的枚舉屬性值為:GREEN 30 System.out.println(annotation.annotationAttr().value());//這里輸出的注解屬性值:gacl 31 32 MetaAnnotation ma = annotation.annotationAttr();//annotation是MyAnnotation類的一個實例對象 33 System.out.println(ma.value());//輸出的結果為:gacl 34 35 36 } 37 } 38 }