詳見地址:http://www.cnblogs.com/xdp-gacl/p/3622275.html
java用 @interface Annotation{ } 定義一個註解 @Annotation,一個註解是一個類
註解(Annotation)很重要,未來的開發模式都是基於註解的,JPA是基於註解的,Spring2.5以上都是基於註解的,Hibernate3.x以後也是基於註解的,現在的Struts2有一部分也是基於註解的了,註解是一種趨勢,現在已經有不少的人開始用註解了,註解是JDK1.5之後纔有的新特性
@Override,@Deprecated,@SuppressWarnings爲常見的3個註解。
註解相當於一種標記,在程序中加上了註解就等於爲程序加上了某種標記,標記可以加在包、類,屬性、方法,方法的參數以及局部變量上
以後,JAVAC編譯器,開發工具和其他程序可以用反射來了解你的類以及各種元素上有無任何標記,看你有什麼標記,就去幹相應的事
註解@Override用在方法上,當我們想重寫一個方法時,在方法上加@Override,當我們方法的名字出錯時,編譯器就會報錯,如圖:
註解@Deprecated,用來表示某個類的屬性或方法已經過時,不想別人再用時,在屬性和方法
上用@Deprecated修飾,如圖:
註解@SuppressWarnings用來壓制程序中出來的警告,比如在沒有用泛型或是方法已經過時的時候,
//這裏就是註解,稱爲壓縮警告,這是JDK內部自帶的一個註解,一個註解就是一個類,在這裏使用了這個註解就是創建了SuppressWarnings類的一個實例對象
如圖:
註解@Retention可以用來修飾註解,是註解的註解,稱爲元註解
Retention註解說明
當在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.)
Retention註解有一個屬性value,是RetentionPolicy類型的,Enum RetentionPolicy是一個枚舉類型,
@Documented註解標記的元素,Javadoc工具會將此註解標記元素的註解信息包含在javadoc中。默認,註解信息不會包含在Javadoc中
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
這個枚舉決定了Retention註解的作用範圍,也可理解爲Rentention 搭配 RententionPolicy使用。RetentionPolicy有3個值:CLASS RUNTIME SOURCE
用@Retention(RetentionPolicy.CLASS)修飾的註解,表示註解的信息被保留在class文件(字節碼文件)中當程序編譯時,但不會被虛擬機讀取在運行的時候;
用@Retention(RetentionPolicy.SOURCE )修飾的註解,表示註解的信息會被編譯器拋棄,不會留在class文件中,註解的信息只會留在源文件中;
用@Retention(RetentionPolicy.RUNTIME )修飾的註解,表示註解的信息被保留在class文件(字節碼文件)中當程序編譯時,會被虛擬機保留在運行時,
所以他們可以用反射的方式讀取。RetentionPolicy.RUNTIME 可以讓你從JVM中讀取Annotation註解的信息,以便在分析程序的時候使用.
package com.self; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface MyTarget { }
定義個一註解@MyTarget,用RetentionPolicy.RUNTIME修飾;
package com.self; import java.lang.reflect.Method; public class MyTargetTest { @MyTarget public void doSomething() { System.out.println("hello world"); } public static void main(String[] args) throws Exception { Method method = MyTargetTest.class.getMethod("doSomething",null); if(method.isAnnotationPresent(MyTarget.class))//如果doSomething方法上存在註解@MyTarget,則爲true { System.out.println(method.getAnnotation(MyTarget.class)); } } }
上面程序打印:@com.self.MyTarget(),如果RetentionPolicy值不爲RUNTIME,則不打印
@Retention(RetentionPolicy.SOURCE ) public @interface Override @Retention(RetentionPolicy.SOURCE ) public @interface SuppressWarnings @Retention(RetentionPolicy.RUNTIME ) public @interface Deprecated
由上可以看出,只有註解@Deprecated在運行時可以被JVM讀取到
註解中可以定義屬性,看例子:
@Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String hello() default "gege"; String world(); int[] array() default { 2, 4, 5, 6 }; EnumTest.TrafficLamp lamp() ; TestAnnotation lannotation() default @TestAnnotation(value = "ddd"); Class style() default String.class; }
上面程序中,定義一個註解@MyAnnotation,定義了6個屬性,他們的名字爲:
hello,world,array,lamp,lannotation,style.
- 屬性hello類型爲String,默認值爲gege
- 屬性world類型爲String,沒有默認值
- 屬性array類型爲數組,默認值爲2,4,5,6
- 屬性lamp類型爲一個枚舉,沒有默認值
- 屬性lannotation類型爲註解,默認值爲@TestAnnotation,註解裏的屬性是註解
- 屬性style類型爲Class,默認值爲String類型的Class類型
看下面例子:定義了一個MyTest類,用註解@MyAnnotation修飾,註解@MyAnnotation定義的屬性都賦了值
@MyAnnotation(hello = "beijing", world="shanghai",array={},lamp=TrafficLamp.RED,style=int.class) public class MyTest { @MyAnnotation(lannotation=@TestAnnotation(value="baby"), world = "shanghai",array={1,2,3},lamp=TrafficLamp.YELLOW) @Deprecated @SuppressWarnings("") public void output() { System.out.println("output something!"); } }
接着通過反射讀取註解的信息:
public class MyReflection { public static void main(String[] args) throws Exception { MyTest myTest = new MyTest(); Class<MyTest> c = MyTest.class; Method method = c.getMethod("output", new Class[] {}); //如果MyTest類名上有註解@MyAnnotation修飾,則爲true if(MyTest.class.isAnnotationPresent(MyAnnotation.class)) { System.out.println("have annotation"); } if (method.isAnnotationPresent(MyAnnotation.class)) { method.invoke(myTest, null); //調用output方法 //獲取方法上註解@MyAnnotation的信息 MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class); String hello = myAnnotation.hello(); String world = myAnnotation.world(); System.out.println(hello + ", " + world);//打印屬性hello和world的值 System.out.println(myAnnotation.array().length);//打印屬性array數組的長度 System.out.println(myAnnotation.lannotation().value()); //打印屬性lannotation的值 System.out.println(myAnnotation.style()); } //得到output方法上的所有註解,當然是被RetentionPolicy.RUNTIME修飾的 Annotation[] annotations = method.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation.annotationType().getName()); } } }
上面程序打印:
have annotation output something! gege, shanghai 3 baby class java.lang.String com.heima.annotation.MyAnnotation java.lang.Deprecated
如果註解中有一個屬性名字叫value,則在應用時可以省略屬性名字不寫。
可見,@Retention(RetentionPolicy.RUNTIME )註解中,RetentionPolicy.RUNTIME是註解屬性值,屬性名字是value,
屬性的返回類型是RetentionPolicy,如下:
public @interface MyTarget { String value(); }
可以這樣用:
@MyTarget("aaa") public void doSomething() { System.out.println("hello world"); }
註解@Target也是用來修飾註解的元註解,它有一個屬性ElementType也是枚舉類型,
//Target註解決定MyAnnotation註解可以加在哪些成分上,如加在類身上,或者屬性身上,或者方法身上等成分
@Target元註解
@Target元註解決定了一個註解可以標識到哪些成分上,如標識在在類身上,或者屬性身上,或者方法身上等成分,@Target默認值爲任何元素(成分)
例如:
1 @Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE}) 2 @Retention(value=SOURCE) 3 public @interface SuppressWarnings
值爲:ANNOTATION_TYPE CONSTRUCTOR FIELD LOCAL_VARIABLE METHOD PACKAGE PARAMETER TYPE
如@Target(ElementType.METHOD) 修飾的註解表示該註解只能用來修飾在方法上
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MyTarget { String value() default "hahaha"; }
如把@MyTarget修飾在類上,則程序報錯,如:
@MyTarget public class MyTargetTest