理解Java中的注解

先看一個(gè)常用注解的小例子:

public class Demo{

//單一注解
@Test
public static void A(){
     System.out.println("Test");
    }
}

//一個(gè)方法上多個(gè)注解
@Deprecated
@SuppressWarnings("uncheck")
public static void B(){
   
    }

通過在方法上使用@Test注解后,在運(yùn)行該方法時(shí),測(cè)試框架會(huì)自動(dòng)識(shí)別該方法并單獨(dú)調(diào)用,@Test實(shí)際上是一種標(biāo)記注解欧穴,起標(biāo)記作用,運(yùn)行時(shí)告訴測(cè)試框架該方法為測(cè)試方法诱咏。而對(duì)于@Deprecated和@SuppressWarnings(“uncheck”)苔可,則是Java本身內(nèi)置的注解,在代碼中袋狞,可以經(jīng)撤俑ǎ看見它們,但這并不是一件好事苟鸯,畢竟當(dāng)方法或是類上面有@Deprecated注解時(shí)同蜻,說明該方法或是類都已經(jīng)過期不建議再用,@SuppressWarnings 則表示忽略指定警告早处,比如@SuppressWarnings(“uncheck”)湾蔓,這就是注解的最簡(jiǎn)單的使用方式.


基本語法:

  • 聲明注解和元注解

我們先來看看前面的Test注解是如何聲明的:
注意:Test內(nèi)部沒有定義其他元素,表示此注解為--標(biāo)記注解(markder annotation)

//聲明Test注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
//內(nèi)部沒有定義其他元素砌梆,表示此注解為標(biāo)記注解(markder annotation)
} 

我們使用了@interface聲明了Test注解默责,并使用@Target注解傳入ElementType.METHOD參數(shù)來標(biāo)明@Test只能用于方法上,@Retention(RetentionPolicy.RUNTIME)則用來表示該注解生存期是運(yùn)行時(shí)咸包,從代碼上看注解的定義很像接口的定義桃序,確實(shí)如此,畢竟在編譯后也會(huì)生成Test.class文件烂瘫。對(duì)于@Target和@Retention是由Java提供的元注解媒熊,所謂元注解就是標(biāo)記其他注解的注解
@Target 用來約束注解可以應(yīng)用的地方(如方法、類或字段)坟比,其中ElementType是枚舉類型芦鳍,其定義如下,也代表可能的取值范圍

public enum ElementType{
TYPE,//標(biāo)明該注解可以用于類葛账、接口(包括注解類型)或enum聲明
FIELD,//標(biāo)明該注解可以用于字段(域)聲明柠衅,包括enum實(shí)例
METHOD,//標(biāo)明該注解可以用于方法聲明
PARAMETER,//標(biāo)明該注解可以用于參數(shù)聲明
CONSTRUCTOR,//標(biāo)明注解可以用于構(gòu)造函數(shù)聲明
LOCAL_VARIABLE,//標(biāo)明注解可以用于局部變量聲明
ANNOTATION_TYPE,//標(biāo)明注解可以用于注解聲明(應(yīng)用于另一個(gè)注解上)
PACKAGE,//標(biāo)明注解可以用于包聲明
/**
** @since 1.8  類型使用聲明,1.8新加入
**
/
  TYPE_PARAMETER,

/**
** @since 1.8  類型使用聲明籍琳,1.8新加入
**
/
TYPE_USE

如果@Target沒有指定具體的值的時(shí)候茄茁,可以綁定多個(gè)枚舉字段,例如:

@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})

@Retention用來約束注解的生命周期巩割,分別有三個(gè)值裙顽,源碼級(jí)別(source),類文件級(jí)別(class)或者運(yùn)行時(shí)級(jí)別(runtime)宣谈,其含有如下:
? ? SOURCE:注解將被編譯器丟棄(該類型的注解信息只會(huì)保留在源碼里愈犹,源碼經(jīng)過編譯后,注解信息會(huì)被丟棄,不會(huì)保留在編譯好的class文件里)
? ?CLASS:注解在class文件中可用漩怎,但會(huì)被VM丟棄(該類型的注解信息會(huì)保留在源碼里和class文件里勋颖,在執(zhí)行的時(shí)候,不會(huì)加載到虛擬機(jī)中)勋锤,請(qǐng)注意饭玲,當(dāng)注解未定義Retention值時(shí),默認(rèn)值是CLASS叁执,如Java內(nèi)置注解茄厘,@Override、@Deprecated谈宛、@SuppressWarnning等
? ?RUNTIME:注解信息將在運(yùn)行期(JVM)也保留次哈,因此可以通過反射機(jī)制讀取注解的信息(源碼、class文件和執(zhí)行的時(shí)候都有注解的信息)吆录,如SpringMvc中的@Controller窑滞、@Autowired、@RequestMapping等恢筝。

  • 注解元素及其數(shù)據(jù)類型

通過上述對(duì)@Test注解的定義哀卫,我們了解了注解定義的過程,由于@Test內(nèi)部沒有定義其他元素撬槽,所以@Test也稱為標(biāo)記注解(marker annotation)聊训,但在自定義注解中,一般都會(huì)包含一些元素以表示某些值恢氯,方便處理器使用,這點(diǎn)在下面的例子將會(huì)看到:

/**
 * Created by wuzejian on 2017/5/18.
 * 對(duì)應(yīng)數(shù)據(jù)表注解
 */
 @Target(ElementType.TYPE)//只能應(yīng)用于類上
 @Retention(RetentionPolicy.RUNTIME)//保存到運(yùn)行時(shí)
 public @interface DBTable { String name() default ""; 
}

上述定義一個(gè)名為DBTable的注解鼓寺,該用于主要用于數(shù)據(jù)庫(kù)表與Bean類的映射(稍后會(huì)有完整案例分析)勋拟,與前面Test注解不同的是,我們聲明一個(gè)String類型的name元素妈候,其默認(rèn)值為空字符敢靡,但是必須注意到對(duì)應(yīng)任何元素的聲明應(yīng)采用方法的聲明方式,同時(shí)可選擇使用default提供默認(rèn)值苦银,@DBTable使用方式如下:

//在類上使用該注解
@DBTable(name = "MEMBER")
public class Member {
    //.......
}

關(guān)于注解支持的元素?cái)?shù)據(jù)類型除了上述的String啸胧,還支持如下數(shù)據(jù)類型

  • 所有基本類型(int,float,boolean,byte,double,char,long,short)
  • String
  • Class
  • enum
  • Annotation
  • 上述類型的數(shù)組

切記

  • 倘若使用了其他數(shù)據(jù)類型,編譯器將會(huì)丟出一個(gè)編譯錯(cuò)誤
  • 聲明注解元素時(shí)可以使用基本類型但不允許使用任何包裝類型
  • 注解也可以作為元素的類型幔虏,也就是嵌套注解纺念,下面的代碼演示了上述類型的使用過程:
package com.zejian.annotationdemo;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy; 
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
 @Retention(RetentionPolicy.RUNTIME) 
@interface Reference{ boolean next() default false; } 
public @interface AnnotationElementDemo {
 //枚舉類型
enum Status {FIXED,NORMAL}; 
//聲明枚舉 
Status status() default Status.FIXED;
 //布爾類型 
boolean showSupport() default false;
 //String類型
 String name()default ""; 
//class類型 
Class<?> testCase() default Void.class;
 //注解嵌套 
Reference reference() default @Reference(next=true); 
//數(shù)組類型 long[] value(); }
  • 編譯器對(duì)默認(rèn)值的限制

    其次,對(duì)于非基本類型的元素想括,無論是在源代碼中聲明陷谱,還是在注解接口中定義默認(rèn)值,都不能以null作為值,這就是限制烟逊,沒有什么利用可言渣窜,但造成一個(gè)元素的存在或缺失狀態(tài),因?yàn)槊總€(gè)注解的聲明中宪躯,所有的元素都存在乔宿,并且都具有相應(yīng)的值,為了繞開這個(gè)限制访雪,只能定義一些特殊的值详瑞,例如空字符串或負(fù)數(shù),表示某個(gè)元素不存在冬阳。

  • 注解不支持繼承

    注解是不支持繼承的蛤虐,因此不能使用關(guān)鍵字extends來繼承某個(gè)@interface,但注解在編譯后肝陪,編譯器會(huì)自動(dòng)繼承java.lang.annotation.Annotation接口驳庭,這里我們反編譯前面定義的DBTable注解

package com.zejian.annotationdemo;
import java.lang.annotation.Annotation; 
//反編譯后的代碼
public interface DBTable extends Annotation{
     public abstract String name(); 

}

雖然反編譯后發(fā)現(xiàn)DBTable注解繼承了Annotation接口,請(qǐng)記住氯窍,即使Java的接口可以實(shí)現(xiàn)多繼承饲常,但定義注解時(shí)依然無法使用extends關(guān)鍵字繼承@interface。

  • 快捷方式(一般不推薦)

所謂的快捷方式就是注解中定義了名為value的元素狼讨,并且在使用該注解時(shí)贝淤,如果該元素是唯一需要賦值的一個(gè)元素,那么此時(shí)無需使用key=value的語法政供,而只需在括號(hào)內(nèi)給出value元素所需的值即可播聪。這可以應(yīng)用于任何合法類型的元素,記住布隔,這限制了元素名必須為value离陶,簡(jiǎn)單案例如下:

package com.zejian.annotationdemo;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//定義注解 
@Target(ElementType.FIELD) 
@Retention(RetentionPolicy.RUNTIME) 
@interface IntegerVaule{
     int value() default 0; String name() default ""; 
} 
//使用注解 public class QuicklyWay {
 //當(dāng)只想給value賦值時(shí),可以使用以下快捷方式 
@IntegerVaule(20) public int age;
 //當(dāng)name也需要賦值時(shí)必須采用key=value的方式賦值 
@IntegerVaule(value = 10000,name = "MONEY") 
public int money; 

}
  • Java內(nèi)置注解與其它元注解

    接著看看Java提供的內(nèi)置注解,主要有3個(gè)衅檀,如下:
    • @Override:用于標(biāo)明此方法覆蓋了父類的方法招刨,源碼如下
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

  • @Deprecated:用于標(biāo)明已經(jīng)過時(shí)的方法或類,源碼如下哀军,關(guān)于@Documented稍后分析
@Documented @Retention(RetentionPolicy.RUNTIME) 
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE}) 

public @interface Deprecated { 

}
  • @SuppressWarnnings:用于有選擇的關(guān)閉編譯器對(duì)類沉眶、方法、成員變量杉适、變量初始化的警告谎倔,其實(shí)現(xiàn)源碼如下:
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) 
@Retention(RetentionPolicy.SOURCE) 
public @interface SuppressWarnings { 
String[] value(); 
}

其內(nèi)部有一個(gè)String數(shù)組,主要接收值如下:
deprecation:使用了不贊成使用的類或方法時(shí)的警告猿推; unchecked:執(zhí)行了未檢查的轉(zhuǎn)換時(shí)的警告传藏,例如當(dāng)使用集合時(shí)沒有用泛型 (Generics) 來指定集合保存的類型; fallthrough:當(dāng) Switch 程序塊直接通往下一種情況而沒有 Break 時(shí)的警告; path:在類路徑、源文件路徑等中有不存在的路徑時(shí)的警告; serial:當(dāng)在可序列化的類上缺少 serialVersionUID 定義時(shí)的警告; finally:任何 finally 子句不能正常完成時(shí)的警告; all:關(guān)于以上所有情況的警告。
這個(gè)三個(gè)注解比較簡(jiǎn)單毯侦,看個(gè)簡(jiǎn)單案例即可:

//注明該類已過時(shí)哭靖,不建議使用 
@Deprecated
class A{ public void A(){ } 
//注明該方法已過時(shí),不建議使用 @Deprecated() public void B(){ } } 
class B extends A{ 
@Override //標(biāo)明覆蓋父類A的A方法 
public void A() { super.A(); }

//去掉檢測(cè)警告 @SuppressWarnings({"uncheck","deprecation"}) 
public void C(){ } 
//去掉檢測(cè)警告 @SuppressWarnings("uncheck") 
public void D(){ } }
  • 前面我們分析了兩種元注解侈离,@Target和@Retention试幽,除了這兩種元注解,Java還提供了另外兩種元注解卦碾,@Documented和@Inherited铺坞,下面分別介紹:
    • Documented 被修飾的注解會(huì)生成到j(luò)avadoc中
@Documented
@Target(ElementType.TYPE) 
@Retention(RetentionPolicy.RUNTIME) 
public @interface DocumentA { } 
//沒有使用@Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME)
public @interface DocumentB { } 
//使用注解 @DocumentA @DocumentB 
public class DocumentDemo { public void A(){ } }

使用javadoc命令生成文檔:
JasonMacBook$ javadoc DocumentDemo.java DocumentA.java DocumentB.java

  • @Inherited 可以讓注解被繼承,但這并不是真的繼承洲胖,只是通過使用@Inherited济榨,可以讓子類Class對(duì)象使用getAnnotations()獲取父類被@Inherited修飾的注解,如下:
@Inherited 
@Documented
@Target(ElementType.TYPE) 
@Retention(RetentionPolicy.RUNTIME) 
public @interface DocumentA { } @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME) 
public @interface DocumentB { } 
@DocumentA 
class A{ } 
class B extends A{ } 
@DocumentB class C{ } 
class D extends C{ } 
//測(cè)試 
public class DocumentDemo { 
public static void main(String... args){ A instanceA=new B();
System.out.println("已使用的@Inherited注解:"+Arrays.toString(instanceA.getClass().getAnnotations()));
C instanceC = new D(); 
System.out.println("沒有使用的@Inherited注解:"+Arrays.toString(instanceC.getClass().getAnnotations())); }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末绿映,一起剝皮案震驚了整個(gè)濱河市擒滑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌叉弦,老刑警劉巖丐一,帶你破解...
    沈念sama閱讀 221,820評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異淹冰,居然都是意外死亡库车,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門樱拴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來柠衍,“玉大人,你說我怎么就攤上這事晶乔≌浞唬” “怎么了?”我有些...
    開封第一講書人閱讀 168,324評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵瘪弓,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我禽最,道長(zhǎng)腺怯,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,714評(píng)論 1 297
  • 正文 為了忘掉前任川无,我火速辦了婚禮呛占,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘懦趋。我一直安慰自己晾虑,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著帜篇,像睡著了一般糙捺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上笙隙,一...
    開封第一講書人閱讀 52,328評(píng)論 1 310
  • 那天洪灯,我揣著相機(jī)與錄音,去河邊找鬼竟痰。 笑死签钩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的坏快。 我是一名探鬼主播铅檩,決...
    沈念sama閱讀 40,897評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼莽鸿!你這毒婦竟也來了昧旨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,804評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤富拗,失蹤者是張志新(化名)和其女友劉穎臼予,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體啃沪,經(jīng)...
    沈念sama閱讀 46,345評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡粘拾,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了创千。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缰雇。...
    茶點(diǎn)故事閱讀 40,561評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖追驴,靈堂內(nèi)的尸體忽然破棺而出械哟,到底是詐尸還是另有隱情,我是刑警寧澤殿雪,帶...
    沈念sama閱讀 36,238評(píng)論 5 350
  • 正文 年R本政府宣布暇咆,位于F島的核電站,受9級(jí)特大地震影響丙曙,放射性物質(zhì)發(fā)生泄漏爸业。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評(píng)論 3 334
  • 文/蒙蒙 一亏镰、第九天 我趴在偏房一處隱蔽的房頂上張望扯旷。 院中可真熱鬧,春花似錦索抓、人聲如沸钧忽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)耸黑。三九已至桃煎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間崎坊,已是汗流浹背备禀。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留奈揍,地道東北人曲尸。 一個(gè)月前我還...
    沈念sama閱讀 48,983評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像男翰,于是被迫代替她去往敵國(guó)和親另患。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容