摘要
Java Annotation是JDK5.0引入的一種注釋機制。
網上很多關于Java Annotation的文章,看得人眼花缭亂。Java Annotation本來很簡單的,結果說的人沒說清楚;弄的看的人更加迷糊。
我按照自己的思路,對Annotation進行了整理。了解 Annotation 的關鍵,是了解Annotation的文法和用法,對這些内容,我都進行了詳細說明;了解Annotation的文法和用法之後,再看Annotation的架構圖,可能有更深刻體會。廢話就說這麼多,下面開始對Annotation進行說明。若您發現文章中存在錯誤或不足的地方,希望您能指出!
第1部分 Annotation架構
先看看Annotation的架構圖:

從中,我們可以看出:
(01) 1個Annotation 和 1個RetentionPolicy關聯。
可以了解為:每1個Annotation對象,都會有唯一的RetentionPolicy屬性。
(02) 1個Annotation 和 1~n個ElementType關聯。
可以了解為:對于每1個Annotation對象,可以有若幹個ElementType屬性。
(03) Annotation 有許多實作類,包括:Deprecated, Documented, Inherited, Override等等。
Annotation 的每一個實作類,都“和1個RetentionPolicy關聯”并且“和1~n個ElementType關聯”。
下面,我先介紹架構圖的左半邊(如下圖),即Annotation, RetentionPolicy, ElementType;然後在就Annotation的實作類進行舉例說明。
第2部分 Annotation組成部分
1 annotation組成成分
java annotation 的組成中,有3個非常重要的主幹類。它們分别是:
(01) Annotation.java
(02) ElementType.java
(03) RetentionPolicy.java
說明:
(01) Annotation 就是個接口。
“每1個Annotation” 都與 “1個RetentionPolicy”關聯,并且與
“1~n個ElementType”關聯。可以通俗的了解為:每1個Annotation對象,都會有唯一的RetentionPolicy屬性;至于
ElementType屬性,則有1~n個。
(02) ElementType 是Enum枚舉類型,它用來指定Annotation的類型。
“每1個Annotation” 都與 “1~n個ElementType”關聯。當Annotation與某個ElementType關聯時,就意味着:Annotation有了某種用途。
例如,若一個Annotation對象是METHOD類型,則該Annotation隻能用來修飾方法。
(03) RetentionPolicy 是Enum枚舉類型,它用來指定Annotation的政策。通俗點說,就是不同RetentionPolicy類型的Annotation的作用域不同。
“每1個Annotation” 都與 “1個RetentionPolicy”關聯。
a) 若Annotation的類型為 SOURCE,則意味着:Annotation僅存在于編譯器處理期間,編譯器處理完之後,該Annotation就沒用了。
例如,“ @Override ”标志就是一個Annotation。當它修飾一個方法的時候,就意味着該方法覆寫父類的方法;并且在編譯期間會進行文法檢查!編譯器處理完後,“@Override”就沒有任何作用了。
b) 若Annotation的類型為 CLASS,則意味着:編譯器将Annotation存儲于類對應的.class檔案中,它是Annotation的預設行為。
c) 若Annotation的類型為 RUNTIME,則意味着:編譯器将Annotation存儲于class檔案中,并且可由JVM讀入。
這時,隻需要記住“每1個Annotation” 都與 “1個RetentionPolicy”關聯,并且與 “1~n個ElementType”關聯。學完後面的内容之後,再回頭看這些内容,會更容易了解。
第3部分 java自帶的Annotation
了解了上面的3個類的作用之後,我們接下來可以講解Annotation實作類的文法定義了。
上面的作用是定義一個Annotation,它的名字是MyAnnotation1。定義了MyAnnotation1之後,我們可以在代碼中通過“@MyAnnotation1”來使用它。
其它的,@Documented, @Target, @Retention, @interface都是來修飾MyAnnotation1的。下面分别說說它們的含義:
(01) @interface
使用@interface定義注解時,意味着它實作了java.lang.annotation.Annotation接口,即該注解就是一個Annotation。
定義Annotation時,@interface是必須的。
注意:它和我們通常的implemented實作接口的方法不同。Annotation接口的實作細節都由編譯器完成。通過@interface定義注解後,該注解不能繼承其他的注解或接口。
(02) @Documented
類和方法的Annotation在預設情況下是不出現在javadoc中的。如果使用@Documented修飾該Annotation,則表示它可以出現在javadoc中。
定義Annotation時,@Documented可有可無;若沒有定義,則Annotation不會出現在javadoc中。
(03) @Target(ElementType.TYPE)
前面我們說過,ElementType 是Annotation的類型屬性。而@Target的作用,就是來指定Annotation的類型屬性。
@Target(ElementType.TYPE) 的意思就是指定該Annotation的類型是ElementType.TYPE。這就意味着,MyAnnotation1是來修飾“類、接口(包括注釋類型)或枚舉聲明”的注解。
定義Annotation時,@Target可有可無。若有@Target,則該Annotation隻能用于它所指定的地方;若沒有@Target,則該Annotation可以用于任何地方。
(04) @Retention(RetentionPolicy.RUNTIME)
前面我們說過,RetentionPolicy 是Annotation的政策屬性,而@Retention的作用,就是指定Annotation的政策屬性。
@Retention(RetentionPolicy.RUNTIME)
的意思就是指定該Annotation的政策是RetentionPolicy.RUNTIME。這就意味着,編譯器會将該Annotation資訊保留
在.class檔案中,并且能被虛拟機讀取。
定義Annotation時,@Retention可有可無。若沒有@Retention,則預設是RetentionPolicy.CLASS。
2 java自帶的Annotation
通過上面的示例,我們能了解:@interface用來聲明Annotation,@Documented用來表示該Annotation是否會出現在javadoc中, @Target用來指定Annotation的類型,@Retention用來指定Annotation的政策。
了解這一點之後,我們就很容易了解java中自帶的Annotation的實作類,即Annotation架構圖的右半邊。如下圖:
java 常用的Annotation:
由于“@Deprecated和@Override”類似,“@Documented, @Inherited, @Retention, @Target”類似;下面,我們隻對@Deprecated, @Inherited, @SuppressWarnings 這3個Annotation進行說明。
2.1 @Deprecated
@Deprecated 的定義如下:
(01) @interface -- 它的用來修飾Deprecated,意味着Deprecated實作了java.lang.annotation.Annotation接口;即Deprecated就是一個注解。
(02) @Documented -- 它的作用是說明該注解能出現在javadoc中。
(03)
@Retention(RetentionPolicy.RUNTIME) --
它的作用是指定Deprecated的政策是RetentionPolicy.RUNTIME。這就意味着,編譯器會将Deprecated的資訊保留
(04) @Deprecated 所标注内容,不再被建議使用。
例如,若某個方法被 @Deprecated 标注,則該方法不再被建議使用。如果有開發人員試圖使用或重寫被@Deprecated标示的方法,編譯器會給相應的提示資訊。示例如下:
源碼如下(DeprecatedTest.java):
View Code
上面是eclipse中的截圖,比較類中 “getString1() 和 getString2()” 以及 “testDate() 和 testCalendar()” 。
(01) getString1() 被@Deprecated标注,意味着建議不再使用getString1();是以getString1()的定義和調用時,都會一橫線。這一橫線是eclipse()對@Deprecated方法的處理。
getString2() 沒有被@Deprecated标注,它的顯示正常。
(02) testDate() 調用了Date的相關方法,而java已經建議不再使用Date操作日期/時間。是以,在調用Date的API時,會産生警告資訊,途中的warnings。
testCalendar() 調用了Calendar的API來操作日期/時間,java建議用Calendar取代Date。是以,操作Calendar不回産生warning。
2.2 @Inherited
@Inherited 的定義如下:
(01) @interface -- 它的用來修飾Inherited,意味着Inherited實作了java.lang.annotation.Annotation接口;即Inherited就是一個注解。
@Retention(RetentionPolicy.RUNTIME) --
它的作用是指定Inherited的政策是RetentionPolicy.RUNTIME。這就意味着,編譯器會将Inherited的資訊保留
(04) @Target(ElementType.ANNOTATION_TYPE) -- 它的作用是指定Inherited的類型是ANNOTATION_TYPE。這就意味着,@Inherited隻能被用來标注“Annotation類型”。
(05) @Inherited 的含義是,它所标注的Annotation将具有繼承性。
假設,我們定義了某個Annotaion,它的名稱是MyAnnotation,并且MyAnnotation被标注為@Inherited。現在,某
個類Base使用了MyAnnotation,則Base具有了“具有了注解MyAnnotation”;現在,Sub繼承了Base,由于
MyAnnotation是@Inherited的(具有繼承性),是以,Sub也“具有了注解MyAnnotation”。
@Inherited的使用示例
源碼如下(InheritableSon.java):
運作結果:
InheritableFather:true
InheritableSon:true
現在,我們對InheritableSon.java進行修改:注釋掉“Inheritable的@Inherited注解”。
InheritableSon:false
對比上面的兩個結果,我們發現:當注解Inheritable被@Inherited标注時,它具有繼承性。否則,沒有繼承性。
2.3 @SuppressWarnings
@SuppressWarnings 的定義如下:
(01) @interface -- 它的用來修飾SuppressWarnings,意味着SuppressWarnings實作了java.lang.annotation.Annotation接口;即SuppressWarnings就是一個注解。
(02)
@Retention(RetentionPolicy.SOURCE) --
它的作用是指定SuppressWarnings的政策是RetentionPolicy.SOURCE。這就意味着,SuppressWarnings
資訊僅存在于編譯器處理期間,編譯器處理完之後SuppressWarnings就沒有作用了。
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
-- 它的作用是指定SuppressWarnings的類型同時包括TYPE, FIELD, METHOD, PARAMETER,
CONSTRUCTOR, LOCAL_VARIABLE。
TYPE意味着,它能标注“類、接口(包括注釋類型)或枚舉聲明”。
FIELD意味着,它能标注“字段聲明”。
METHOD意味着,它能标注“方法”。
PARAMETER意味着,它能标注“參數”。
CONSTRUCTOR意味着,它能标注“構造方法”。
LOCAL_VARIABLE意味着,它能标注“局部變量”。
(04) String[] value(); 意味着,SuppressWarnings能指定參數
(05)
SuppressWarnings
的作用是,讓編譯器對“它所标注的内容”的某些警告保持靜默。例如,"@SuppressWarnings(value={"deprecation",
"unchecked"})" 表示對“它所标注的内容”中的
“SuppressWarnings不再建議使用警告”和“未檢查的轉換時的警告”保持沉默。示例如下:
源碼如下(SuppressWarningTest.java):
(01) 左邊的圖中,沒有使用 @SuppressWarnings(value={"deprecation"}) , 而Date屬于java不再建議使用的類。是以,調用Date的API時,會産生警告。
而右邊的途中,使用了 @SuppressWarnings(value={"deprecation"})。是以,編譯器對“調用Date的API産生的警告”保持沉默。
補充:SuppressWarnings 常用的關鍵字的表格
第4部分 Annotation 的作用
Annotation 是一個輔助類,它在Junit、Struts、Spring等工具架構中被廣泛使用。
我們在程式設計中經常會使用到的Annotation作用有:
1 編譯檢查
Annotation具有“讓編譯器進行編譯檢查的作用”。
例如,@SuppressWarnings, @Deprecated和@Override都具有編譯檢查作用。
(01) 關于@SuppressWarnings和@Deprecated,已經在“第3部分”中詳細介紹過了。這裡就不再舉例說明了。
(02) 若某個方法被 @Override的 标注,則意味着該方法會覆寫父類中的同名方法。如果有方法被@Override标示,但父類中卻沒有“被@Override标注”的同名方法,則編譯器會報錯。示例如下:
源碼(OverrideTest.java):
上面是該程式在eclipse中的截圖。從中,我們可以發現“getString()”函數會報錯。這是因為“getString() 被@Override所标注,但在OverrideTest的任何父類中都沒有定義getString1()函數”。
“将getString() 上面的@Override注釋掉”,即可解決該錯誤。
2 在反射中使用Annotation
在反射的Class, Method, Field等函數中,有許多于Annotation相關的接口。
這也意味着,我們可以在反射中解析并使用Annotation。
源碼如下(AnnotationTest.java):
somebody: lily, 18
girl, boy,
@com.skywang.annotation.MyAnnotation(value=[girl, boy])
empty
unknown,
@com.skywang.annotation.MyAnnotation(value=[unknown])
@java.lang.Deprecated()
3 根據Annotation生成幫助文檔
通過給Annotation注解加上@Documented标簽,能使該Annotation标簽出現在javadoc中。
4 能夠幫忙檢視檢視代碼
通過@Override, @Deprecated等,我們能很友善的了解程式的大緻結構。
另外,我們也可以通過自定義Annotation來實作一些功能。