在上一篇文章中,我們介紹了注解(Annotation)
的相關(guān)概念,對于 java1.5 引入的這種新特性,我們可以稱其為一種特殊的注釋。之所以說它特殊是因?yàn)椴煌谄胀ㄗ⑨?code>(comment)只能存在于源碼涝滴,而且還能在程序的編譯期跟運(yùn)行期中存在,會(huì)最終編譯成一個(gè).class文件。理所應(yīng)當(dāng)歼疮,注解會(huì)比普通注解起到更多的作用杂抽。
注解為我們在代碼中添加信息提供了一種形式化的方法,使我們可以在稍后某個(gè)時(shí)刻方便地使用這些數(shù)據(jù)(通過 解析注解 來使用這些數(shù)據(jù))韩脏。要想深入的理解并使用注解缩麸,必須能定義自己的注解,所以了解java
提供的注解定義語法就非常重要了赡矢。
包java.lang.annotation
中包含所有定義自定義注解所需用到的元注解和接口杭朱。比如接口java.lang.annotation.Annotation
是所有注解都繼承的接口,并且是自動(dòng)繼承,不需要定義時(shí)指定吹散,類似于所有類都自動(dòng)繼承Object弧械。首先我們先來了解什么是元注解。
元注解
元注解是 Java 本身提供空民,是專門用來定義注解的注解刃唐。被用來提供對其他注解類型作說明。Java提供了四個(gè)標(biāo)準(zhǔn)的元注解類型:
- @Target,
- @Retention,
- @Documented,
- @Inherited
@Target
@Target
標(biāo)識(shí) Annotation 可以被使用在什么地方界轩。
使用方式:@Target(ElementType.METHOD)
画饥。其可能的 ElementType 取值包括:
- CONSTRUCTOR --> 構(gòu)造器(構(gòu)造函數(shù))的聲明
- FIELD ---> 域聲明,包括 ENUM(枚舉)實(shí)例
- LOCAL_VIRIEBLE ---> 局部變量聲明
- METHOD ---> 方法聲明
- PACKAGE ---> 包聲明
- PARAMETER ---> 參數(shù)聲明
- *TYPE ---> 類浊猾,接口聲明抖甘。包括注解類型和ENUM類型
@Retention
決定了 Annotation 的生命周期,即被保留的時(shí)間長短或者說在什么范圍內(nèi)有效与殃。Annotation 一共有三種生命周期单山,有的注解只出現(xiàn)在源代碼中;有的注解被被編譯在class文件中幅疼,但被虛擬機(jī)忽略;有的則是被虛擬機(jī)加載昼接,一直到程序運(yùn)行都還存在爽篷。
使用方式:@Retention(RetentionPolicy = RUNTIME)。其可能的 **RetentionPolicy ** 取值包括:
- SOURCE -> 在源文件中有效(即源文件保留)
- CLASS -> 在class文件中有效(即class保留)
- RUNTIME -> 在運(yùn)行時(shí)有效(即運(yùn)行時(shí)保留)
@Documented
描述 Annotation 應(yīng)該被作為被標(biāo)注的程序成員的公共API慢睡,因此可以被例如javadoc此類的工具文檔化逐工。Documented是一個(gè)標(biāo)記注解,沒有成員漂辐。被例如javadoc此類的工具文檔泪喊。
@Inherited
這也是一個(gè)標(biāo)記注解。
@Inherited
闡述了某個(gè)被標(biāo)注的類型是被繼承的髓涯。被標(biāo)注過的 class 的子類所繼承袒啼。類并不從它所實(shí)現(xiàn)的接口繼承 Annotation,方法并不從它所重載的方法繼承 Annotation。
當(dāng)使用@Inherited
類型標(biāo)注的 Annotation 的 Retention 是RetentionPolicy.RUNTIME蚓再,則反射API增強(qiáng)了這種繼承性滑肉。如果我們使用java.lang.reflect去查詢一個(gè)@Inherited annotation類型的annotation時(shí),反射代碼檢查將展開工作:檢查class和其父類摘仅,直到發(fā)現(xiàn)指定的annotation類型被發(fā)現(xiàn)靶庙,或者到達(dá)類繼承結(jié)構(gòu)的頂層。
在本文的開頭我們就說過娃属,定義注解時(shí)會(huì)自動(dòng)繼承java.lang.annotation.Annotation
接口六荒。同時(shí)還要注意的是,在定義注解時(shí)不能繼承其他注解和接口矾端。
注解的定義相對簡單掏击,有點(diǎn)類似于定義接口:
public @interface TestAnnotation {
String field1() default "it's empty"; //default 設(shè)置默認(rèn)值
}
其中的每個(gè)方法都相當(dāng)于聲明了一個(gè)配置參數(shù),參數(shù)的名稱就是方法名须床,參數(shù)的類型就是返回值類型铐料。在自定義注解時(shí)有一些注意事項(xiàng):
- 方法(配置參數(shù))只能用 public 或者 默認(rèn)(default)這兩種訪問權(quán)限修飾。
- 方法(配置參數(shù))的返回值支持類型只包括以下幾種:
- 所有的數(shù)據(jù)類型(
int豺旬,float钠惩,boolean,byte族阅,double篓跛,char,long坦刀,short
) - String類型
- Class類型
- enum類型
- Annotation(注解)類型
- 以上所有類型的數(shù)組
- 所有的數(shù)據(jù)類型(
- 注解中有一個(gè)特殊的存在:value愧沟。當(dāng)注解中只有一個(gè)方法(配置參數(shù))時(shí),最好是將方法命名為 value鲤遥。這樣在使用注解時(shí)沐寺,就可以省略參數(shù)名,比如之后的例子:
@Owner
盖奈。
簡單的自定義注解使用例子
/**
* book相關(guān)的注解混坞。
* 包括兩個(gè)參數(shù):名稱和頁數(shù)
**/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BookInfo {
public String name() default "";
public int pageSize() default 0;
}
/**
* 擁有人注解
* 只有一個(gè)參數(shù),命名為 value
**/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Owner {
public String value() default "";
}
public class UserBook {
@BookInfo(name = "EngLish", pageSize = 80)
public EngLish engLish;
@Owner("testName")
public String ownerName;
}
注解元素必須有確定的值钢坦,要么在定義注解的默認(rèn)值中指定究孕,要么在使用注解時(shí)指定,非基本類型的注解元素的值不可為null爹凹。
在注解的語法部分中厨诸,對于特殊的 value 參數(shù),當(dāng)只指定該參數(shù)時(shí)禾酱,可以省略參數(shù)名和等號(hào)微酬。這也是上面說的注意事項(xiàng)中第三點(diǎn)的原因
根據(jù)注解參數(shù)的不同绘趋,可以將注解分為以下幾類:
- 標(biāo)注注解。沒有參數(shù)成員得封,通過判斷該注解是否存在獲得相關(guān)的信息
- 單值注解埋心。只有一個(gè)參數(shù)成員,一般為 value忙上,這樣可以省略參數(shù)名和等號(hào)
- 完整注解