初步了解Annotation(注解)

看了郭神公眾號推薦的注解知識,想到自己這方面有欠缺,大家一起來學(xué)習(xí).本文主要是對自己學(xué)到的知識進行總結(jié),和大家分享自己學(xué)到的東西.這里給出我看的文章地址一小時搞明白自定義注解.我讀完了腦袋還是蒙的,不是作者寫的不好,而是讀完了腦子蒙蒙的沒有啥印象..所以我百度看了一下相關(guān)知識,看了瘋狂Java的注解(第14章),結(jié)合平常見到的一些注解(主要是Android上用的多的),寫了這篇總結(jié).讓我可以更好的理解注解.

一.凡事先問為什么---為什么要有注解,注解又是什么東西?###

主要是概念性的東西也要了解.面試要用啊.

Annotation(注解)是什么?####

從JDK5也就是Java5.0開始,Java增加了對元數(shù)據(jù)(MetaData)的支持,也就是Annotation注解.這里所介紹的Annotation其實是代碼里的特殊標記,這些標記可以在編譯、類加載悲幅、運行時被讀取,并進行相應(yīng)的處理.Annotation是一個接口,程序可以通過反射來獲取指定程序元素的Annotation對象,然后通過Annotation對象來取得注解里的元數(shù)據(jù).

這是通用解釋.看完了也只知道這個是用來干嘛的,還是一知半解,往下繼續(xù).

為什么要有注解?####

通過使用注解,程序開發(fā)人員可以在不改變原有邏輯的情況下,在源文件中嵌入一些補充信息.代碼分析工具、開發(fā)工具和部署工具可以通過這些補充信息進行驗證或者進行部署.Annotation能被用來為程序元素(類暑竟、方法粥诫、成員變量)設(shè)置元數(shù)據(jù),但是并不影響代碼的執(zhí)行,無論增加幔荒、刪除Annotation,代碼都始終如一的執(zhí)行,如果希望讓程序中的Annotation在運行時起到一定的作用需要通過某種配套工具(統(tǒng)稱APT)對Annotation的信息進行訪問和處理.

二.知道是Annotation(注解)是什么,然后就要知道這個東西本身有什么.###

JDK中的元Annotation(注解).元注解的作用就是負責(zé)注解其他注解.位于java.lang.annotation包下.

基本的Annotation(注解)有5個.分別是@Retention,@Target,@Documented,@Inherited,@Repeatable(這個專門用于定義Java8新增的重復(fù)注解,不常用就先不說了,需要的可自行查詢).

@Retention####

用于指定被修飾的Annotation(注解)可以保留多長時間.@Retention包含一個RetentionPolicy類型的value成員變量,所以使用的時候必須為該value成員變量指定固定的值

1.@Retention(RetentionPolicy.Class)

即class保留.也是默認值. 編譯器將Annotation記錄在class中.當運行Java程序時,JVM不可獲取Annotation的信息.

查了網(wǎng)上的信息和解釋,沒人說這東西有什么用

2.@Retention(RetentionPolicy.SOURCE)

即源代碼保留.編譯器直接丟棄這種Annotation.那這種有什么用.查了下可以用來提示IDE.這東西在android開發(fā)中經(jīng)吵叮看到,可能很多人都沒有關(guān)注.看下面代碼


@Target(ElementType.METHOD)

@Retention(RetentionPolicy.SOURCE)

public @interface Override{

}

可能有的人就問了,你這都是啥啥啥.這東西可是非常的常見.再看

代碼

<pre>

@Override

protected voidonStart(){

super.onStart();

}

</pre>

發(fā)現(xiàn)沒有.這個點進去就是. @Retention(RetentionPolicy.SOURCE).我覺得一般這種既然是放在源代碼中的,我們暫時不需要去關(guān)心,只需要了解即可.現(xiàn)在是水平研究這個沒什么用,比較費勁.

3.@Retention(RetentionPolicy.RUNTIME)

即運行時保留.也是網(wǎng)上現(xiàn)階段認為比較有用的東西.主要是用來干嘛的呢.編譯器同樣會把Annotation記錄在class文件中,當運行Java程序時,JVM也可以獲取Annotation信息,程序可以通過反射獲取該Annotation信息.一般都是用在通過反射獲取注解信息這一用途.具體的不是這篇文章追究的,但是大家有興趣和精力可以去了解一下,比較是這個屬性里最有用的東西.

PS:不是單指這一個元注解.注解指定value值的形式一般是value=變量值的形式.但是如果使用注解時只需要為value成員變量指定值,則使用該注解時可以直接在該注解后的括號里指定value成員變量的值.

@Target####

這被用于指定被修飾的Annotation能用于修飾哪些程序單元.就是指定注解能放在什么地方.@Target是元注解,只能修飾注解,不能直接修飾在方法啥箭、類等上.這點要注意.同理介紹的這四個都是如此.要理解這點.

這里可以看上面代碼有一個@Target 可以看到@Override只能放在方法上.因為@Target里的值為METNOD.

以下為@Target的所可選的值(前綴統(tǒng)一為ElementType.)

1.CONSTRUCTOR: 用于描述構(gòu)造器

  1. FIELD:用于描述域.這里比較模糊.查了書上的說明是指定Annotation只能修飾成員變量.

  2. LOCAL_VARIABLE:用于描述局部變量

  3. METHOD:用于描述方法

  4. PACKAGE:用于描述包

  5. PARAMETER:用于描述參數(shù)

  6. TYPE:用于描述類、接口(包括注解類型) 或enum(枚舉)聲明

  7. ANNOTATION: 指定該策略的Annotation只能修飾Annotation.也就是成了一個元注解.

@Documened####

@Documented用于描述其它類型的annotation應(yīng)該被作為被標注的程序成員的公共API治宣,因此可以被例如javadoc此類的工具文檔化急侥。Documented是一個標記注解,沒有成員侮邀。.我在android開發(fā)中碰見過 但是不得其解.

@Inherited

@Inherited元注解是一個標記注解坏怪,@Inherited闡述了某個被標注的類型是被繼承的。如果一個使用了@Inherited修飾的annotation類型被用于一個class绊茧,則這個annotation將被用于該class的子類铝宵。

注意:@Inherited annotation類型是被標注過的class的子類所繼承。類并不從它所實現(xiàn)的接口繼承annotation华畏,方法并不從它所重載的方法繼承annotation鹏秋。

看到這里我也是一陣糊涂,這到底是啥意思.看下面的一個解釋示例就大概懂了.

當@Inherited annotation類型標注的annotation的Retention是RetentionPolicy.RUNTIME時,則反射API增強了這種繼承性唯绍。如果我們使用java.lang.reflect去查詢一個@Inherited annotation類型的annotation時拼岳,反射代碼檢查將展開工作:檢查class和其父類,直到發(fā)現(xiàn)指定的annotation類型被發(fā)現(xiàn)况芒,或者到達類繼承結(jié)構(gòu)的頂層惜纸。

PS:這個我也沒用過,因為上面的解釋涉及到反射.我反射基礎(chǔ)不行,還得回頭補充.這里就不隨便去寫誤導(dǎo)大家.有基礎(chǔ)好的可以查閱資料看看.等我反射和反射注解理解了,可能會在另一篇里對這個進行試驗.為什么還要寫出來呢.我從我的水平和理解出發(fā),展示我學(xué)習(xí)Annotation時碰到的問題和我的理解,也是我也這篇文章的原因.

三.了解一些基本的Annotation###

這個就不是元注解了.位于java.lang包下.java提供了5個基本Annotation的用法.使用Annotation時要在其前面加@符號,并把Annotation當成一個修飾符使用,用于修飾它支持的程序元素.

5個基本的Annotation有:@Override@Deprecated@Suppress Warnings@Safe Varargs@FunctionalInterface

1.@Override 限定重寫父類的方法.####

這個東西在平常開發(fā)中非常常見.在這之前我只是知道這是個系統(tǒng)方法.@Override實際上是用來指定方法覆載的,它可以強制一個子類必須覆蓋父類的方法.主要的作用是告訴編譯器檢查這個方法.保證父類包含一個被該方法重寫的方法,否則就會編譯出錯.對于程序員來說,主要是幫助程序員避免一些低級錯誤.

看@Override的代碼可以知道@Override被@Target(ElementType.METHOD)修飾,所以@Override只能用來修飾方法.注解信息被保存在源文件中.

2.@Deprecated 標示已過時.####

<pre>

@Documented

@Retention(RetentionPolicy.RUNTIME)

public @interface Deprecated{

}

</pre>

這個注解會在運行時有效,會引起編譯器的警告.那如何抑制編譯器的警告呢.

3.@SuppressWarnings 抑制編譯器警告.####

這個東西大家也不陌生.@SuppressWarnings會一直作用于該程序元素的所有子元素.大家可能注意到每次使用這個注解,系統(tǒng)都會提示是放在方法上還是某個類上面.反正什么地方,@SuppressWarnings的作用范圍就是什么.這里我注意到,當我在一個方法上添加@SuppressWarnings注解時,系統(tǒng)有如下提示


可以到看@SuppressWarnings里的值是一個數(shù)組類型的.所以說這里是可以放置多個值共同作用的.

在瘋狂Java中我看到作者說@SuppressWarnings的值必須寫成name=value格式(也就是注解值的標準格式),但是我在實際中見到的并不是,比如說寫成@SuppressWarnings("deprecation")-->抑制過時警告. 就可以這么寫

其實這里有一個是系統(tǒng)幫你做了.就是name省略了.name可以省略的前提是注解里只有一個變量.我們可以看下@SuppressWarnings的代碼

<pre>

@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD,

ElementType.PARAMETER,ElementType.CONSTRUCTOR,

ElementType.LOCAL_VARIABLE})

@Retention(RetentionPolicy.SOURCE)

public @interface SuppressWarnings{

/**

  • The list of warnings a compiler should not issue.

*/

public String[] value();

}

</pre>

可以看到里面只有一個value,所以說其實直接寫值就可以了.

4.@SafeVarargs Java7"堆污染"警告和@Functionallnterface Java8的函數(shù)式接口####

這兩個知道有這東西就行了.這里普及一下函數(shù)式接口.Java8中規(guī)定:如果接口中只有一個抽象方法(可以包含多個默認方法或者多個static方法),該接口就是函數(shù)式接口.@Functionallnterface就是用來指定某個接口必須是函數(shù)式接口.函數(shù)式接口就是為Java8的Lambda準備的,Java8允許使用Lambda表達式創(chuàng)建

PS:以下主要知識均來自瘋狂java,其實以上也差不多,哈哈.總結(jié)嘛.######

四.學(xué)以致用,實現(xiàn)自定義Annotation###

1.定義不帶成員變量的Annotation####

定義Annotation需要使用到@interface關(guān)鍵字

定義一個簡單的注解

<pre>

//定義一個簡單的注解

public @interface test{

}

</pre>

這種注解是可以放在程序的任何地方,因為注解上沒有任何元注解修飾.當然我們這么寫這個注解一點用都沒有@Override是因為系統(tǒng)已經(jīng)處理過了.

2. 定義帶成員變量的Annotation####

<pre>

public @interface MyTag{

//定義帶兩個成員變量的Annotation

//Annotation中的成員變量以方法的形式來定義

String name();

int age();

}

</pre>

一旦Annotation中定義了成員變量,使用的時候就必須為成員變量賦值.

同時可以指定Annotation的默認值

<pre>

public @interface MyTag{

//定義帶兩個成員變量的Annotation

//Annotation中的成員變量以方法的形式來定義

Stringname() default "haha";

int age() default 9;

}

</pre>

這樣就可以在使用的時候不指定值,一旦指定了值,將會覆蓋默認值.

五.提取Annotation信息###

使用Annotation修飾,這些Annotation不會自己生效,必須由開發(fā)者提供相應(yīng)的工具來提取并處理Annotation信息.

跳過書里的一些說明直接給出方法.

AnnotatedElement接口提供的抽象方法(在該接口的實現(xiàn)類中重寫了這些方法):

方法1.<A extends Annotation> getAnnotation(Class<A> annotationClass)######

<A extends Annotation>為泛型參數(shù)聲明,表明A的類型只能是Annotation類型或者是Annotation的子類绝骚。

返回該程序元素上存在的耐版、指定類型的注解,如果該類型的注解不存在压汪,則返回null

方法2. Annotation[] getAnnotations()######

返回此元素上存在的所有注解粪牲,包括沒有顯示定義在該元素上的注解(繼承得到的)。(如果此元素沒有注釋止剖,則返回長度為零的數(shù)組腺阳。)

方法3. <A extends Annotation> getDeclaredAnnotation(Class<A> annotationClass)######

這是Java8新增的方法,該方法返回直接修飾該程序元素穿香、指定類型的注解(忽略繼承的注解)亭引。如果該類型的注解不存在,返回null.

方法4. Annotation[] getDeclaredAnnotations()######

返回直接存在于此元素上的所有注解皮获,該方法將忽略繼承的注釋焙蚓。(如果沒有注釋直接存在于此元素上,則返回長度為零的一個數(shù)組。)

方法5. boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)######

判斷該程序元素上是否存在指定類型的注解购公,如果存在則返回true,否則返回false萌京。

方法6. <A extends Annotation>T[] getAnnotationsByTpye(Class<A> annotationClass)######

因為java8增加了重復(fù)注解功能,因此需要使用該方法獲得修飾該程序元素宏浩、指定類型的多個注解知残。

方法7. <A extends Annotation>T[] getDeclaredAnnotationsByTpye(Class<A> annotationClass)######

因為java8增加了重復(fù)注解功能,因此需要使用該方法獲得直接修飾該程序元素绘闷、指定類型的多個注解橡庞。

這些方法不要刻意記住,大概有個印象就可以了.

用一個例子看一下.和書上的例子差不多.######

<pre>

//注解 注意這里必須加上這個Retention屬性,并且值必須是RUNTIME.只有這樣才能通過反射拿到值

@Retention(RetentionPolicy.RUNTIME)

public @ interface test{

String name() default"111";

}

</pre>

<pre>
//tes類
public class tes{
@test
public void info(){}
}
</pre>

<pre>

//隨便找一個Activity

@Override

protected void onResume(){

super.onResume();

try{

//這里一點要寫完整的類,不然會報找不到類的異常

 Annotation[]  array=Class.forName("com.example.wyfsh.myapplication.tes").getMethod("info").getAnnotations();

 for(Annotation annotation:array){

 Log.i("=================>",annotation+"");

}

}catch(Exceptione){

e.printStackTrace();

}

</pre>

輸出結(jié)果.@com.example.wyfsh.myapplication.test() 輸出的結(jié)果為tes類下info方法上所有的注解.

如果需要獲取某個注解里的元數(shù)據(jù),則需要將注解類型強制轉(zhuǎn)換成所需的注解類型.然后通過注解對象的抽象方法來訪問這些數(shù)據(jù).

比如上面的獲取test注解里的name.就可以這么寫


@Override
protected void onResume(){
super.onResume();
try{

      Annotation[] array=Class.forName("com.example.wyfsh.myapplication.tes").getMethod("info").getAnnotations();
      for(Annotation annotation:array){
            if(annotation instanceof test){
            Log.i("=================>",((test)annotation).name()+"");
      }

}

}catch(Exceptione){
e.printStackTrace();
}

}

打印結(jié)果:111

最后關(guān)于使用實例.建議去結(jié)合buttutter knife去學(xué)習(xí).本來還有很多概念要說,但是比較啰嗦.所以結(jié)合這個基礎(chǔ)知識去看buttutter knife的實現(xiàn)原理更有效.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市印蔗,隨后出現(xiàn)的幾起案子扒最,更是在濱河造成了極大的恐慌,老刑警劉巖华嘹,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吧趣,死亡現(xiàn)場離奇詭異,居然都是意外死亡耙厚,警方通過查閱死者的電腦和手機强挫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來薛躬,“玉大人俯渤,你說我怎么就攤上這事⌒捅Γ” “怎么了八匠?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長趴酣。 經(jīng)常有香客問我梨树,道長,這世上最難降的妖魔是什么岖寞? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任抡四,我火速辦了婚禮,結(jié)果婚禮上仗谆,老公的妹妹穿的比我還像新娘指巡。我一直安慰自己,他們只是感情好隶垮,可當我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布厌处。 她就那樣靜靜地躺著,像睡著了一般岁疼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天捷绒,我揣著相機與錄音瑰排,去河邊找鬼。 笑死暖侨,一個胖子當著我的面吹牛椭住,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播字逗,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼京郑,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了葫掉?” 一聲冷哼從身側(cè)響起些举,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎俭厚,沒想到半個月后户魏,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡挪挤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年叼丑,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扛门。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡鸠信,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出论寨,到底是詐尸還是另有隱情星立,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布政基,位于F島的核電站贞铣,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏沮明。R本人自食惡果不足惜辕坝,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望荐健。 院中可真熱鬧酱畅,春花似錦、人聲如沸江场。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽址否。三九已至餐蔬,卻和暖如春碎紊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背樊诺。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工仗考, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人词爬。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓秃嗜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親顿膨。 傳聞我的和親對象是個殘疾皇子锅锨,可洞房花燭夜當晚...
    茶點故事閱讀 43,465評論 2 348

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