Java 注解完全解析

關(guān)于注解首先引入官方文檔的一句話:Java 注解用于為 Java 代碼提供元數(shù)據(jù)悔常。作為元數(shù)據(jù)球订,注解不直接影響你的代碼執(zhí)行筝家,但也有一些類型的注解實際上可以用于這一目的轧钓。Java 注解是從 Java5 開始添加到 Java 的序厉。看完這句話也許你還是一臉懵逼毕箍,接下我將從注解的定義弛房、元注解、注解屬性而柑、自定義注解文捶、注解解析JDK 提供的注解這幾個方面再次了解注解(Annotation)

我自己是一個從事了6年的Java全棧工程師荷逞,最近整理了一套適合2019年學(xué)習(xí)的Java\大數(shù)據(jù)資料,從基礎(chǔ)的Java粹排、大數(shù)據(jù)面向?qū)ο蟮竭M(jìn)階的框架知識都有整理哦种远,可以來我的主頁免費(fèi)領(lǐng)取哦

注解的定義

日常開發(fā)中新建Java類,我們使用class顽耳、interface比較多坠敷,而注解和它們一樣,也是一種類的類型射富,他是用的修飾符為 @interface

注解類的寫法最近整理了一套適合2019年學(xué)習(xí)的Java\大數(shù)據(jù)資料膝迎,從基礎(chǔ)的Java、大數(shù)據(jù)面向?qū)ο蟮竭M(jìn)階的框架知識都有整理哦胰耗,可以來我的主頁免費(fèi)領(lǐng)取哦限次。

我們新建一個注解MyTestAnnotation

public@interfaceMyTestAnnotation{}復(fù)制代碼

接著我們就可以在類或者方法上作用我們剛剛新建的注解

@MyTestAnnotationpublicclasstest{@MyTestAnnotationpublicstaticvoidmain(String[] args){? }}復(fù)制代碼

以上我們只是了解了注解的寫法,但是我們定義的注解中還沒寫任何代碼宪郊,現(xiàn)在這個注解毫無意義掂恕,要如何使注解工作呢?接下來我們接著了解元注解弛槐。

元注解

元注解顧名思義我們可以理解為注解的注解懊亡,它是作用在注解中,方便我們使用注解實現(xiàn)想要的功能乎串。元注解分別有@Retention店枣、 @Target、 @Document叹誉、 @Inherited和@Repeatable(JDK1.8加入)五種鸯两。

@Retention

Retention英文意思有保留、保持的意思长豁,它表示注解存在階段是保留在源碼(編譯期)钧唐,字節(jié)碼(類加載)或者運(yùn)行期(JVM中運(yùn)行)。在@Retention注解中使用枚舉RetentionPolicy來表示注解保留時期

@Retention(RetentionPolicy.SOURCE)匠襟,注解僅存在于源碼中钝侠,在class字節(jié)碼文件中不包含

@Retention(RetentionPolicy.CLASS), 默認(rèn)的保留策略酸舍,注解會在class字節(jié)碼文件中存在帅韧,但運(yùn)行時無法獲得

@Retention(RetentionPolicy.RUNTIME), 注解會在class字節(jié)碼文件中存在啃勉,在運(yùn)行時可以通過反射獲取到

如果我們是自定義注解忽舟,則通過前面分析,我們自定義注解如果只存著源碼中或者字節(jié)碼文件中就無法發(fā)揮作用,而在運(yùn)行期間能獲取到注解才能實現(xiàn)我們目的叮阅,所以自定義注解中肯定是使用?@Retention(RetentionPolicy.RUNTIME)

@Retention(RetentionPolicy.RUNTIME)public@interfaceMyTestAnnotation {}復(fù)制代碼

@Target

Target的英文意思是目標(biāo)刁品,這也很容易理解,使用@Target元注解表示我們的注解作用的范圍就比較具體了帘饶,可以是類哑诊,方法,方法參數(shù)變量等及刻,同樣也是通過枚舉類ElementType表達(dá)作用類型

@Target(ElementType.TYPE) 作用接口镀裤、類、枚舉缴饭、注解

@Target(ElementType.FIELD) 作用屬性字段暑劝、枚舉的常量

@Target(ElementType.METHOD) 作用方法

@Target(ElementType.PARAMETER) 作用方法參數(shù)

@Target(ElementType.CONSTRUCTOR) 作用構(gòu)造函數(shù)

@Target(ElementType.LOCAL_VARIABLE)作用局部變量

@Target(ElementType.ANNOTATION_TYPE)作用于注解(@Retention注解中就使用該屬性)

@Target(ElementType.PACKAGE) 作用于包

@Target(ElementType.TYPE_PARAMETER) 作用于類型泛型,即泛型方法颗搂、泛型類担猛、泛型接口 (jdk1.8加入)

@Target(ElementType.TYPE_USE) 類型使用.可以用于標(biāo)注任意類型除了 class (jdk1.8加入)

一般比較常用的是ElementType.TYPE類型

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public@interfaceMyTestAnnotation {}復(fù)制代碼

@Documented

Document的英文意思是文檔。它的作用是能夠?qū)⒆⒔庵械脑匕?Javadoc 中去丢氢。

@Inherited

Inherited的英文意思是繼承傅联,但是這個繼承和我們平時理解的繼承大同小異,一個被@Inherited注解了的注解修飾了一個父類疚察,如果他的子類沒有被其他注解修飾蒸走,則它的子類也繼承了父類的注解。

下面我們來看個@Inherited注解例子

/**自定義注解*/@Documented@Inherited@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public@interfaceMyTestAnnotation{}/**父類標(biāo)注自定義注解*/@MyTestAnnotationpublicclassFather{}/**子類*/publicclassSonextendsFather{}/**測試子類獲取父類自定義注解*/publicclasstest{? public static void main(String[] args){//獲取Son的class對象Class sonClass =Son.class;// 獲取Son類上的注解MyTestAnnotation可以執(zhí)行成功MyTestAnnotationannotation = sonClass.getAnnotation(MyTestAnnotation.class);? }}復(fù)制代碼

@Repeatable

Repeatable的英文意思是可重復(fù)的貌嫡。顧名思義說明被這個元注解修飾的注解可以同時作用一個對象多次比驻,但是每次作用注解又可以代表不同的含義。

下面我們看一個人玩游戲的例子

/**一個人喜歡玩游戲岛抄,他喜歡玩英雄聯(lián)盟别惦,絕地求生,極品飛車夫椭,塵埃4等掸掸,則我們需要定義一個人的注解,他屬性代表喜歡玩游戲集合蹭秋,一個游戲注解扰付,游戲?qū)傩源碛螒蛎Q*//**玩家注解*/@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public@interfacePeople {? ? Game[] value() ;}/**游戲注解*/@Repeatable(People.class)@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public@interfaceGame {? ? String value()default"";}/**玩游戲類*/@Game(value ="LOL")@Game(value ="PUBG")@Game(value ="NFS")@Game(value ="Dirt4")publicclassPlayGame{}復(fù)制代碼

通過上面的例子,你可能會有一個疑問感凤,游戲注解中括號的變量是啥悯周,其實這和游戲注解中定義的屬性對應(yīng)粒督。接下來我們繼續(xù)學(xué)習(xí)注解的屬性陪竿。

注解的屬性

通過上一小節(jié)@Repeatable注解的例子,我們說到注解的屬性。注解的屬性其實和類中定義的變量有異曲同工之處族跛,只是注解中的變量都是成員變量(屬性)闰挡,并且注解中是沒有方法的,只有成員變量礁哄,變量名就是使用注解括號中對應(yīng)的參數(shù)名长酗,變量返回值注解括號中對應(yīng)參數(shù)類型。相信這會你應(yīng)該會對上面的例子有一個更深的認(rèn)識桐绒。而@Repeatable注解中的變量則類型則是對應(yīng)Annotation(接口)的泛型Class夺脾。

/**注解Repeatable源碼*/@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public@interfaceRepeatable {/**? ? * Indicates thecontaining annotation typeforthe? ? * repeatable annotation type.? ? * @return thecontaining annotation type? ? */Class value();}復(fù)制代碼

注解的本質(zhì)

注解的本質(zhì)就是一個Annotation接口

/**Annotation接口源碼*/publicinterfaceAnnotation{? ? ? boolean equals(Objectobj);inthashCode();? ? ? ? Class annotationType();}復(fù)制代碼

通過以上源碼,我們知道注解本身就是Annotation接口的子接口茉继,?也就是說注解中其實是可以有屬性和方法咧叭,但是接口中的屬性都是static final的,對于注解來說沒什么意義烁竭,而我們定義接口的方法就相當(dāng)于注解的屬性菲茬,也就對應(yīng)了前面說的為什么注解只有屬性成員變量,其實他就是接口的方法派撕,這就是為什么成員變量會有括號?婉弹,不同于接口我們可以在注解的括號中給成員變量賦值。

注解屬性類型

注解屬性類型可以有以下列出的類型

1.基本數(shù)據(jù)類型

2.String

3.枚舉類型

4.注解類型

5.Class類型

6.以上類型的一維數(shù)組類型

注解成員變量賦值

如果注解又多個屬性终吼,則可以在注解括號中用“镀赌,”號隔開分別給對應(yīng)的屬性賦值,如下例子衔峰,注解在父類中賦值屬性

@Documented@Inherited@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public@interfaceMyTestAnnotation {? ? String name()default"mao";? ? int age()default18;}@MyTestAnnotation(name ="father",age = 50)publicclassFather{}復(fù)制代碼

獲取注解屬性

前面我們說了很多注解如何定義佩脊,放在哪,現(xiàn)在我們可以開始學(xué)習(xí)注解屬性的提取了垫卤,這才是使用注解的關(guān)鍵威彰,獲取屬性的值才是使用注解的目的。

如果獲取注解屬性穴肘,當(dāng)然是反射啦歇盼,主要有三個基本的方法

/**是否存在對應(yīng) Annotation 對象*/publicboolean isAnnotationPresent(ClassannotationClass){returnGenericDeclaration.super.isAnnotationPresent(annotationClass);? ? }/**獲取 Annotation 對象*/public A getAnnotation(ClassannotationClass){? ? ? ? Objects.requireNonNull(annotationClass);return(A) annotationData().annotations.get(annotationClass);? ? }/**獲取所有 Annotation 對象數(shù)組*/publicAnnotation[] getAnnotations() {returnAnnotationParser.toArray(annotationData().annotations);? ? }? ? 復(fù)制代碼

下面結(jié)合前面的例子,我們來獲取一下注解屬性评抚,在獲取之前我們自定義的注解必須使用元注解@Retention(RetentionPolicy.RUNTIME)

publicclasstest {publicstaticvoidmain(String[] args)throwsNoSuchMethodException {/**

? ? ? ? * 獲取類注解屬性

? ? ? ? */Class fatherClass = Father.class;booleanannotationPresent = fatherClass.isAnnotationPresent(MyTestAnnotation.class);if(annotationPresent){? ? ? ? ? ? MyTestAnnotation annotation = fatherClass.getAnnotation(MyTestAnnotation.class);? ? ? ? ? ? System.out.println(annotation.name());? ? ? ? ? ? System.out.println(annotation.age());? ? ? ? }/**

? ? ? ? * 獲取方法注解屬性

? ? ? ? */try{? ? ? ? ? ? Field age = fatherClass.getDeclaredField("age");booleanannotationPresent1 = age.isAnnotationPresent(Age.class);if(annotationPresent1){? ? ? ? ? ? ? ? Age annotation = age.getAnnotation(Age.class);? ? ? ? ? ? ? ? System.out.println(annotation.value());? ? ? ? ? ? }? ? ? ? ? ? Method play = PlayGame.class.getDeclaredMethod("play");if(play!=null){? ? ? ? ? ? ? ? People annotation2 = play.getAnnotation(People.class);? ? ? ? ? ? ? ? Game[] value = annotation2.value();for(Game game : value) {? ? ? ? ? ? ? ? ? ? System.out.println(game.value());? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? }catch(NoSuchFieldException e) {? ? ? ? ? ? e.printStackTrace();? ? ? ? }? ? }}復(fù)制代碼

運(yùn)行結(jié)果:

JDK 提供的注解

注解作用注意事項

@Override它是用來描述當(dāng)前方法是一個重寫的方法豹缀,在編譯階段對方法進(jìn)行檢查jdk1.5中它只能描述繼承中的重寫,jdk1.6中它可以描述接口實現(xiàn)的重寫,也能描述類的繼承的重寫

@Deprecated它是用于描述當(dāng)前方法是一個過時的方法無

@SuppressWarnings對程序中的警告去除慨代。無

注解作用與應(yīng)用

現(xiàn)在我們再次回頭看看開頭官方文檔的那句描述

Java 注解用于為 Java 代碼提供元數(shù)據(jù)邢笙。作為元數(shù)據(jù),注解不直接影響你的代碼執(zhí)行侍匙,但也有一些類型的注解實際上可以用于這一目的氮惯。

經(jīng)過我們前面的了解,注解其實是個很方便的東西,它存活的時間妇汗,作用的區(qū)域都可以由你方便設(shè)置帘不,只是你用注解來干嘛的問題

使用注解進(jìn)行參數(shù)配置

下面我們看一個銀行轉(zhuǎn)賬的例子,假設(shè)銀行有個轉(zhuǎn)賬業(yè)務(wù)杨箭,轉(zhuǎn)賬的限額可能會根據(jù)匯率的變化而變化寞焙,我們可以利用注解靈活配置轉(zhuǎn)賬的限額,而不用每次都去修改我們的業(yè)務(wù)代碼互婿。

/**定義限額注解*/@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public@interfaceBankTransferMoney {doublemaxMoney()default10000;}/**轉(zhuǎn)賬處理業(yè)務(wù)類*/publicclassBankService{/**? ? * @param money 轉(zhuǎn)賬金額? ? */@BankTransferMoney(maxMoney =15000)? ? publicstaticvoidTransferMoney(doublemoney){? ? ? ? System.out.println(processAnnotationMoney(money));? ? }? ? privatestaticStringprocessAnnotationMoney(doublemoney) {try{? ? ? ? ? ? Method transferMoney = BankService.class.getDeclaredMethod("TransferMoney",double.class);? ? ? ? ? ? boolean annotationPresent = transferMoney.isAnnotationPresent(BankTransferMoney.class);if(annotationPresent){? ? ? ? ? ? ? ? BankTransferMoney annotation = transferMoney.getAnnotation(BankTransferMoney.class);doublel = annotation.maxMoney();if(money>l){return"轉(zhuǎn)賬金額大于限額捣郊,轉(zhuǎn)賬失敗";? ? ? ? ? ? ? ? }else{return"轉(zhuǎn)賬金額為:"+money+",轉(zhuǎn)賬成功";? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? }catch( NoSuchMethodException e) {? ? ? ? ? ? e.printStackTrace();? ? ? ? }return"轉(zhuǎn)賬處理失敗";? ? }? ? publicstaticvoidmain(String[] args){? ? ? ? TransferMoney(10000);? ? }}復(fù)制代碼

運(yùn)行結(jié)果:

通過上面的例子慈参,只要匯率變化模她,我們就改變注解的配置值就可以直接改變當(dāng)前最大限額。

第三方框架的應(yīng)用

作為一個Android 開發(fā)者懂牧,平常我們所使用的第三方框架ButterKnife侈净,Retrofit2,Dagger2等都有注解的應(yīng)用僧凤,如果我們要了解這些框架的原理畜侦,則注解的基礎(chǔ)知識則是必不可少的。

注解的作用

提供信息給編譯器: 編譯器可以利用注解來檢測出錯誤或者警告信息躯保,打印出日志旋膳。

編譯階段時的處理: 軟件工具可以用來利用注解信息來自動生成代碼、文檔或者做其它相應(yīng)的自動處理途事。

運(yùn)行時處理: 某些注解可以在程序運(yùn)行的時候接受代碼的提取验懊,自動做相應(yīng)的操作。

正如官方文檔的那句話所說尸变,注解能夠提供元數(shù)據(jù)义图,轉(zhuǎn)賬例子中處理獲取注解值的過程是我們開發(fā)者直接寫的注解提取邏輯,?處理提取和處理 Annotation 的代碼統(tǒng)稱為 APT(Annotation Processing Tool)?召烂。上面轉(zhuǎn)賬例子中的processAnnotationMoney方法就可以理解為APT工具類碱工。

最近整理了一套適合2019年學(xué)習(xí)的Java\大數(shù)據(jù)資料,從基礎(chǔ)的Java奏夫、大數(shù)據(jù)面向?qū)ο蟮竭M(jìn)階的框架知識都有整理哦怕篷,可以來我的主頁免費(fèi)領(lǐng)取哦。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末酗昼,一起剝皮案震驚了整個濱河市廊谓,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌麻削,老刑警劉巖蒸痹,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件舔示,死亡現(xiàn)場離奇詭異,居然都是意外死亡电抚,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門竖共,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蝙叛,“玉大人,你說我怎么就攤上這事公给〗枇保” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵淌铐,是天一觀的道長肺然。 經(jīng)常有香客問我,道長腿准,這世上最難降的妖魔是什么际起? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮吐葱,結(jié)果婚禮上街望,老公的妹妹穿的比我還像新娘。我一直安慰自己弟跑,他們只是感情好灾前,可當(dāng)我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著孟辑,像睡著了一般哎甲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上饲嗽,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天炭玫,我揣著相機(jī)與錄音,去河邊找鬼貌虾。 笑死础嫡,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的酝惧。 我是一名探鬼主播榴鼎,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼晚唇!你這毒婦竟也來了巫财?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤哩陕,失蹤者是張志新(化名)和其女友劉穎平项,沒想到半個月后赫舒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡闽瓢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年接癌,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扣讼。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡缺猛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出椭符,到底是詐尸還是另有隱情荔燎,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布销钝,位于F島的核電站有咨,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蒸健。R本人自食惡果不足惜座享,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望似忧。 院中可真熱鬧征讲,春花似錦、人聲如沸橡娄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽挽唉。三九已至滤祖,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間瓶籽,已是汗流浹背匠童。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留塑顺,地道東北人汤求。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像严拒,于是被迫代替她去往敵國和親扬绪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,802評論 2 345

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

  • 一 注解的定義 注解(Annotation)裤唠,也叫元數(shù)據(jù)挤牛。一種代碼級別的說明。它是JDK1.5及以后版本引入的一個...
    _那個人閱讀 3,231評論 0 3
  • 什么是注解(Annotation):Annotation(注解)就是Java提供了一種元程序中的元素關(guān)聯(lián)任何信息和...
    九尾喵的薛定諤閱讀 3,143評論 0 2
  • 從JDK5開始种蘸,Java增加了Annotation(注解)墓赴,Annotation是代碼里的特殊標(biāo)記竞膳,這些標(biāo)記可以在...
    CarlosLynn閱讀 551評論 0 2
  • java自定義注解 Java注解是附加在代碼中的一些元信息,用于一些工具在編譯诫硕、運(yùn)行時進(jìn)行解析和使用坦辟,起到說明、配...
    尼爾君閱讀 516評論 0 0
  • 親子日記第七篇 今天周六兒子不用上學(xué)章办,但是昨天晚上一個勁的叮囑我锉走,今天早上一定要早...
    銘媽媽閱讀 274評論 0 3