從JDK5.0開始, Java增加了對元數據(MetaData)的支持,也就是 Annotation(注解)。
Annotation其實就是代碼里的特殊標記,它用于替換配置文件,也就是說,傳統方式通過配置文件告知類如何運行,有了注解技術后,開發人員可以通過注解告知類如何運行。在Java技術里注解的典型利用是:可以通過反射技術去得到類里面的注解,以決定怎樣去運行類。
掌握注解技術的要點:
@SuppressWarnings:抑制編譯器正告。
首先編寫1個AnnotationTest類,先通過@SuppressWarnings
的利用讓大家認識和了解1下注解,通過System.runFinalizersOnExit(true);
的編譯正告引出@SuppressWarnings("deprecation")
。
public class AnnotationTest {
@SuppressWarnings("deprecation")
public static void main(String[] args) {
System.runFinalizersOnExit(true);
}
}
@Deprecated:用于表示某個程序元素(類,方法等)已過時。
接著直接在剛才的類中增加1個方法,并加上@Deprecated
標注,在另外1個類中調用這個方法。
public class AnnotationTest {
@SuppressWarnings("deprecation")
public static void main(String[] args) {
System.runFinalizersOnExit(true);
}
@Deprecated
public static void sayHello() {
System.out.println("hi,李阿昀");
}
}
public boolean equals(Reflect other)
方法與HashSet結合講授。總結:注解相當于1種標記,在程序中加了注解就等于為程序打上了某種標記,沒加,則等于沒有某種標記,以后,javac編譯器,開發工具和其他程序可以用反射來了解你的類及各種元素上有沒有何種標記,看你有甚么標記,就去干相應的事。標記可以加在包,類,字段,方法,方法的參數和局部變量上。
注解就相當于1個你的源程序中要調用的1個類,要在源程序中利用某個注解,得先準備好了這個注解類,就像你要調用某個類,得先要開發好這個類。
定義新的Annotation類型使用@interface關鍵字。
定義1個最簡單的注解:
public @interface ItcastAnnotation {
}
把它加在某個類上:
@ItcastAnnotation
public class AnnotationTest {
@SuppressWarnings("deprecation")
public static void main(String[] args) {
System.runFinalizersOnExit(true);
}
@Deprecated
public static void sayHello() {
System.out.println("hi,李阿昀");
}
}
用反射進行測試AnnotationTest的定義上是不是有@ItcastAnnotation:
@ItcastAnnotation
public class AnnotationTest {
@SuppressWarnings("deprecation")
public static void main(String[] args) {
System.runFinalizersOnExit(true);
if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)) { // 類上是不是有注解,默許情況下返回false
ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);
System.out.println(annotation);
}
}
@Deprecated
public static void sayHello() {
System.out.println("hi,李阿昀");
}
}
發現控制臺無任何反應,根據反射測試的問題,引出@Retention
元注解的講授。
@Retention元注解
只能用于修飾1個Annotation的定義, 用于指定該Annotation可以保存的域,@Rentention包括1個RetentionPolicy類型的成員變量,通過這個變量指定域。其有3種取值:
分別對應:java源文件→class文件→內存中的字節碼。也即1個Java類具有3種狀態:
編譯 JVM裝載進內存
.java -------------------> .class -------------------> 運行時(內存里面的java類)
對@Retention元注解更加細致的解釋:
所以,1個注解的生命周期有3個階段。
我們定義的簡單注解修改成以下:
@Retention(RetentionPolicy.RUNTIME)
public @interface ItcastAnnotation {
}
就可以正確打印了。
結論:我們寫1個注解的目的,主要是用來替換配置文件,我們希望這個類在運行時取得注解配置的信息,來運行我這個類,想要注解配置的信息能夠讓類在運行時獲得到,那就1定要把這個注解聲明在運行時。
思考:@Override、@SuppressWarnings和@Deprecated這3個注解的屬性值分別是甚么?
答:
@Override→RetetionPolicy.SOURCE
@SuppressWarnings→RetetionPolicy.SOURCE
@Deprecated→RetetionPolicy.RUNTIME
@Target元注解
指定注解用于修飾類的哪一個成員。@Target包括了1個名為value,類型為ElementType的成員變量。如若聲明注解時,沒指定@Target,默許該注解可以作用在類的所有成員上。
Target的默許值為任何元素,設置Target等于ElementType.METHOD
,原來加在類上的注解就報錯了,改成用數組方式設置{ElementType.METHOD,ElementType.TYPE}
就能夠了,表示此注解既可以在方法上使用,也可在類上使用。
注意:元注解和其枚舉屬性值不用記,只要會看JDK提供那幾個基本注解的API幫助文檔的定義或其源代碼,按圖索驥便可查到,或直接看java.lang.annotation包下面的類。
注解屬性的作用:原來寫在配置文件中的信息,可以通過注解的屬性進行描寫。
@ItcastAnnotation(color="red")
定義基本類型的屬性和利用屬性
在注解類中增加String color()
:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface ItcastAnnotation {
String color();
}
用反射方式取得注解對應的實例對象后,再通過該對象調用屬性對應的方法:
@ItcastAnnotation(color="red")
public class AnnotationTest {
@SuppressWarnings("deprecation")
public static void main(String[] args) {
System.runFinalizersOnExit(true);
if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)) { // 類上是不是有注解,默許情況下返回false
ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);
System.out.println(annotation.color()); // red
}
}
@Deprecated
public static void sayHello() {
System.out.println("hi,李阿昀");
}
}
可以認為上面這個@ItcastAnnotation(color=”red”)是ItcastAnnotation類的1個實例對象。
為屬性指定缺省值:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface ItcastAnnotation {
String color() default "blue";
}
value屬性:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface ItcastAnnotation {
String color() default "blue";
String value();
}
用反射方式取得注解對應的實例對象后,再通過該對象調用屬性對應的方法:
@ItcastAnnotation(color="red", value="abc")
public class AnnotationTest {
@SuppressWarnings("deprecation")
@ItcastAnnotation("xyz")
public static void main(String[] args) {
System.runFinalizersOnExit(true);
if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)) { // 類上是不是有注解,默許情況下返回false
ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);
System.out.println(annotation.value()); // abc
}
}
@Deprecated
public static void sayHello() {
System.out.println("hi,李阿昀");
}
}
注意:如果注解中有1個名稱為value的屬性,且你只想設置value屬性(即其他屬性都采取默許值或你只有1個value屬性),那末可以省略value=
部份,例如:@ItcastAnnotation("lhm")
。
數組類型的屬性
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface ItcastAnnotation {
String color() default "blue";
String value();
int[] arrayAttr() default {3,4,4};
}
用反射方式取得注解對應的實例對象后,再通過該對象調用屬性對應的方法:
@ItcastAnnotation(color="red", value="abc", arrayAttr=1)
public class AnnotationTest {
@SuppressWarnings("deprecation")
@ItcastAnnotation("xyz")
public static void main(String[] args) {
System.runFinalizersOnExit(true);
if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)) { // 類上是不是有注解,默許情況下返回false
ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);
System.out.println(annotation.arrayAttr().length); // 1
}
}
@Deprecated
public static void sayHello() {
System.out.println("hi,李阿昀");
}
}
注意:如果數組屬性中只有1個元素,這時候候屬性值部份可以省略大括號。
枚舉類型的屬性
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface ItcastAnnotation {
String color() default "blue";
String value();
int[] arrayAttr() default {3,4,4};
EnumTest.TrafficLamp lamp() default EnumTest.TrafficLamp.RED;
}
用反射方式取得注解對應的實例對象后,再通過該對象調用屬性對應的方法:
@ItcastAnnotation(color="red", value="abc", arrayAttr=1)
public class AnnotationTest {
@SuppressWarnings("deprecation")
@ItcastAnnotation("xyz")
public static void main(String[] args) {
System.runFinalizersOnExit(true);
if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)) { // 類上是不是有注解,默許情況下返回false
ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);
System.out.println(annotation.lamp().nextLamp().name()); // GREEN
}
}
@Deprecated
public static void sayHello() {
System.out.println("hi,李阿昀");
}
}
注解類型的屬性:
我們再定義1個注解MetaAnnotation
:
public @interface MetaAnnotation {
String value();
}
接著再定義1個注解類型的屬性:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface ItcastAnnotation {
String color() default "blue";
String value();
int[] arrayAttr() default {3,4,4};
EnumTest.TrafficLamp lamp() default EnumTest.TrafficLamp.RED;
MetaAnnotation annotationAttr() default @MetaAnnotation("lhm");
}
枚舉和注解都是特殊的類,不能用new創建它們的實例對象,創建枚舉的實例對象就是在其中增加元素。在程序中如何創建出1個注解的實例對象啊?直接用@放上1個標記便可,例如:@MetaAnnotation("lhm")
。
用反射方式取得注解對應的實例對象后,再通過該對象調用屬性對應的方法:
@ItcastAnnotation(annotationAttr=@MetaAnnotation("flx"), color="red", value="abc", arrayAttr=1)
public class AnnotationTest {
@SuppressWarnings("deprecation")
@ItcastAnnotation("xyz")
public static void main(String[] args) {
System.runFinalizersOnExit(true);
if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)) { // 類上是不是有注解,默許情況下返回false
ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);
System.out.println(annotation.annotationAttr().value()); // flx
}
}
@Deprecated
public static void sayHello() {
System.out.println("hi,李阿昀");
}
}
可以認為@ItcastAnnotation是ItcastAnnotation類的1個實例對象,一樣的道理,可以認為@MetaAnnotation是MetaAnnotation類的1個實例對象。
注解的詳細語法可以通過看java語言規范了解,即看java的language specification,可知道注解的屬性類型包括:基本數據類型,String,Class,枚舉,其他注解,和這些類型的數組。