看了郭神公眾號推薦的注解知識,想到自己這方面有欠缺,大家一起來學(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)造器
FIELD:用于描述域.這里比較模糊.查了書上的說明是指定Annotation只能修飾成員變量.
LOCAL_VARIABLE:用于描述局部變量
METHOD:用于描述方法
PACKAGE:用于描述包
PARAMETER:用于描述參數(shù)
TYPE:用于描述類、接口(包括注解類型) 或enum(枚舉)聲明
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)原理更有效.