目的
學完本文章你將會明白什么是Annotation宛官,Annotation的基礎聲明與使用滑燃。
什么是注解嵌削?
要回答這個問題蚣驼,我們先來看一個案例羔砾。
@Override
public String toString() {
return "This is String.";
}
在上面代碼中:@Override 就是一個注解负间。
那么我現(xiàn)在來回答你什么是注解;注解是從Java1.5版本開始加入Java大家族的姜凄,目的是給代碼提供一定的注釋唉擂、標注。Annotation是一種應用于類檀葛、方法玩祟、參數(shù)、變量屿聋、構造器及包聲明中的特殊修飾符空扎。它是一種由JSR-175標準選擇用來描述元數(shù)據的一種工具。
起初Annotation的出現(xiàn)是為了簡化代碼的XML配置與描述润讥,用于簡化這一過程转锈。
除了@Override這一注解之外,我們常用的還有:
@Override
@Deprecated
SuppressWarnings
以上3中是咱們代碼中最常見的3中基礎注解楚殿。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
@Override 是為了標注方法是復寫父類或是實現(xiàn)的接口撮慨,那么編譯器會為我們在編譯時做檢測,檢查是否在父類或集成的接口中有該方法脆粥,已達到避免出錯的目的砌溺。上面的代碼如果我們不加@Override然后改成 toString(int a) 也是可以編譯過,但是這就與我們的初心違背了变隔。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
@Deprecated是不受待見的规伐,因為它的出現(xiàn)意味著類或方法是過時的,將要廢棄的匣缘;我們的代碼應當給具有更加優(yōu)秀的方案替換的老舊的類或方法加上這樣的注解猖闪,已表明這方法是過時的鲜棠,使用者應當使用更好的方案。
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
@SuppressWarnings與上面2者基礎注解不同培慌,他具有一個接收值豁陆;該值是一個數(shù)組。主要的包括:
- all吵护,抑制所有警告
- boxing献联,抑制與封裝/拆裝作業(yè)相關的警告
- cast,抑制與強制轉型作業(yè)相關的警告
- dep-ann何址,抑制與淘汰注釋相關的警告
- deprecation,抑制與淘汰的相關警告
- fallthrough进胯,抑制與switch陳述式中遺漏break相關的警告
- finally用爪,抑制與未傳回finally區(qū)塊相關的警告
- hiding,抑制與隱藏變數(shù)的區(qū)域變數(shù)相關的警告
- incomplete-switch胁镐,抑制與switch陳述式(enum case)中遺漏項目相關的警告
- javadoc偎血,抑制與javadoc相關的警告
- nls,抑制與非nls字串文字相關的警告
- null盯漂,抑制與空值分析相關的警告
- rawtypes颇玷,抑制與使用raw類型相關的警告
- resource,抑制與使用Closeable類型的資源相關的警告
- restriction就缆,抑制與使用不建議或禁止參照相關的警告
- serial帖渠,抑制與可序列化的類別遺漏serialVersionUID欄位相關的警告
- static-access,抑制與靜態(tài)存取不正確相關的警告
- static-method竭宰,抑制與可能宣告為static的方法相關的警告
- super空郊,抑制與置換方法相關但不含super呼叫的警告
- synthetic-access,抑制與內部類別的存取未最佳化相關的警告
- sync-override切揭,抑制因為置換同步方法而遺漏同步化的警告
- unchecked狞甚,抑制與未檢查的作業(yè)相關的警告
- unqualified-field-access,抑制與欄位存取不合格相關的警告
- unused廓旬,抑制與未用的程式碼及停用的程式碼相關的警告
注解的作用
一般而言注解具有如下作用:
- 純粹的標示哼审、注釋。比如給你的代碼加上一個自定義的注解:@QIUJUER孕豹;這個注解有意義么涩盾?有意義,意義就在于表明這個代碼是我寫的励背;這個解釋我服E陨蕖!
- 生成代碼文檔椅野,用于輔助代碼文檔生產终畅,以及編譯工具的查閱籍胯。例:
/
**
* @param count Count
* @return String
* @see #start(int)
* @since 1.2.0
*/
public String start(int count) {
return null;
}
實現(xiàn)代碼跟蹤,簡化配置文件离福,簡化XML文件的使用杖狼。長用于Web開發(fā)。
編譯時進行代碼跟蹤妖爷,比如@Override
編譯時進行輔助代碼生成蝶涩;如注入框架Butterknife,數(shù)據庫框架DbFlow
運行時進行邏輯替換或叫做功能完善絮识;比如Retrofit
注解的語法
上面簡單的介紹了注解绿聘,而且明白了什么是注解,那么注解該如何聲明次舌?
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* @author qiujuer Email:qiujuer@live.cn
* @version 1.0.0
*/
@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface JumpUiThread {
JumpType value() default JumpType.AUTO;
}
以上的代碼展示了一個完整的自定義注解聲明熄攘,現(xiàn)在我們來剖析一下。
注解的聲明非常簡單與接口的方式類似彼念,只不過在interface前面加上一個 “@” 符號即可挪圾。至于注解中的JumpType這個僅僅是一個枚舉而已,隨后將會講到逐沙。
元注解
這個名字很特別哲思,在開篇的時候我們講了Java的基礎注解,OK記住哪些都是 基礎注解 吩案,是Java基礎的時候提供的棚赔;那么何為元注解?
“用于注解其他注解的注解叫做元注解徘郭∫涫龋” 他的本質也是注解,只不過相對特殊是用于注解其他的注解而已崎岂。
- @Documented:注解是否將包含在JavaDoc中
- @Retention:什么時候使用該注解
- @Target:注解用于什么地方
- @Inherited:是否允許子類繼承該注解
- @Repeatable:Java8引入捆毫,用于標示可以重復使用的注解
上面的5種注解都是元注解,也都是Java內置具備的冲甘;其中最后一項是Java8才加入的绩卤。在上面的例子中我們展示了其中3種元注解;下面我們來詳細介紹一下江醇。
@Documented
使用@Documented修飾的注解將會在JavaDoc輸出的時候保留該注解濒憋。一般用于需要讓閱讀文檔的人知道有這個注解,或者是這個注解需要讓接手文檔的人知曉陶夜;注解對邏輯流程具有一定的作用或干擾性凛驮。
public class Test {
@Documented
public @interface Annotation1 {
}
public @interface Annotation2 {
}
@Annotation1
public static class Foo1 {
}
@Annotation2
public static class Foo2 {
}
}
對于這個例子我們使用命令 “javadoc Test.java" 生成文檔。
![](https://qiujuer.gitbooks.io/android-annotation-guide/content/assets/about/EB8A1C49-2C9C-4188-A038-4C24B344FA53.png)
可以發(fā)現(xiàn)Foo1和Foo2文檔并不相同条辟,一個帶有注解信息黔夭,一個未帶有宏胯。
@Retention
注解保留到何時,用于約束注解的生命周期本姥,有如下3種選擇(枚舉):
- RetentionPolicy.SOURCE:注解僅僅保留在源碼級別肩袍,編譯后class文件中將去除。一般用于編譯時校驗和代碼生產使用婚惫。
- RetentionPolicy.CLASS:注解保留到class級別氛赐,但是JVM加載時將忽略不加載相關信息。常見的:@Override先舷、@Deprecated艰管、@SuppressWarnning,以上注解在你依賴一個已編譯好的lib時依然生效蒋川。
- RetentionPolicy.RUNTIME:注解保留到運行時態(tài)牲芋,此時JVM會加載注解相關信息;你可以在運行時通過代碼獲取class type信息拿到尔破。常見的如:運行時數(shù)據庫框架或注入框架。
@Target
注解可以注解的類型浇衬,用于約束注解可以應用的地方(方法懒构、參數(shù)等)。其枚舉ElementType類型如下:
- ANNOTATION_TYPE 適用于注解類型聲明(元注解就是很好的例子)
- CONSTRUCTOR 適用于構造函數(shù)聲明
- FIELD 適用于字段聲明(包括枚舉常數(shù))(常用)
- LOCAL_VARIABLE 適用于局部變量聲明(常用)
- METHOD 適用于方法聲明(常用)
- PACKAGE 適用于包(package)聲明
- PARAMETER 適用于方法參數(shù)聲明(常用)
- TYPE 適用于類耘擂,接口(包括注釋類型)或枚舉聲明(常用)
- TYPE_PARAMETER Java8新增胆剧,表示該注解能寫在類型變量的聲明語句中
- TYPE_USE Java8新增,表示該注解能寫在使用類型的任何語句中(例如聲明語句醉冤、泛型和強制轉換語句中的類型)
對于最后的:TYPE_PARAMETER 和 TYPE_USE 是Java8所新增的內容秩霍,咱們放到后續(xù)文章中詳細講解,延伸:Checker Framework蚁阳。
@Inherited
標示注解是否可繼承铃绒;這里的集成并不是說注解相互的繼承,而是說父類注解能否被子類所感知螺捐。
public class Test {
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Annotation1 {
}
@Retention(RetentionPolicy.RUNTIME)
@interface Annotation2 {
}
@Annotation1
static class Foo1 {
}
static class Foo2 extends Foo1 {
}
@Annotation2
static class Foo3 {
}
static class Foo4 extends Foo3 {
}
public static void main(String args[]) {
System.out.println("具有@Inherited:" + Arrays.toString(Foo2.class.getAnnotations()));
System.out.println("沒有@Inherited:" + Arrays.toString(Foo4.class.getAnnotations()));
}
}
運行結果:
QIUJUERBOOK:main qiujuer$ java net.qiujuer.jumper.annotation.Test
具有@Inherited:[@net.qiujuer.jumper.annotation.Test$Annotation1()]
沒有@Inherited:[]
@Repeatable
該元注解是Java8才引入的注解颠悬;用于標示一個注解是否可以重復使用。
@Retention(RetentionPolicy.RUNTIME)
@interface Todo {
String value();
}
// 錯誤的案例
@Todo("main")
@Todo("show")
static class Foo {
}
在以前你是沒法如此使用的定血,因為編譯器不給予編譯赔癌;那么我想要Todo多條信息怎么辦?澜沟?
@Retention(RetentionPolicy.RUNTIME)
@interface Todo {
String[] value();
}
@Todo({"main", "show"})
static class Foo {
}
OK, 改成數(shù)組之后正常了灾票,但是Java8允許更加特殊的方法來使用。
@Repeatable(TodoList.class)
@Retention(RetentionPolicy.RUNTIME)
@interface Todo {
String value();
}
@Retention(RetentionPolicy.RUNTIME)
@interface TodoList {
Todo[] value();
}
@Todo("main")
@Todo("show")
static class Foo {
}
上述案例此時是合法的茫虽,但是僅在Java8下能成功運行刊苍;其原理是通過一個具有Annotation Array的注解(TodoList)來接收數(shù)據既们。其獲取的方式咱們也后續(xù)另起文章講解。
好了班缰,到此咱們的注解基礎語法講解完成了贤壁;注解是知道是什么了,但是注解如何使用呢埠忘?
注解的使用
一般而言注解常常會和反射一起使用脾拆;因為注解的信息是存儲在AnnotatedElement中,AnnotatedElement是一個接口莹妒,實現(xiàn)其接口的有:Class名船、Constructor、Field旨怠、Method渠驼、Package。
在實際的使用中鉴腻,我們會使用“java.lang.reflect”包下的一些反射API來獲取上述類的實例迷扇,通過上述對象的實例我們可以使用AnnotatedElement提供的接口方法來訪問標注的Annotaion信息。
- <T extends Annotation> T getAnnotation(Class<T> var1):該元素如果存在指定類型(var1)的注解爽哎,則返回這些注解蜓席;否則返回null。
- boolean isAnnotationPresent(Class<? extends Annotation> var1):如果該元素上存在該注解(var1)則返回True课锌,反之返回False厨内;內部實現(xiàn)是調用的第一個接口判斷是否為null。
- Annotation[] getAnnotations():返回該元素上存在的所有的注解渺贤。包括父類的注解雏胃,前提是具有@Inherited元注解的存在。
- Annotation[] getDeclaredAnnotations():返回該元素上直接的注解志鞍,不包括父類的注解信息瞭亮。這是與getAnnotations()最直接的區(qū)別。
- <T extends Annotation> T getDeclaredAnnotation(Class<T> var1):Java8新增固棚,與第一個方法類似街州,僅僅限定在當前類上,不掃描父類玻孟。
- <T extends Annotation> T[] getAnnotationsByType(Class<T> var1):Java8新增唆缴,與@Repeatable配合使用,返回一個數(shù)組黍翎,包括父類的注解面徽。
- <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> var1):Java8新增,與@Repeatable配合使用,返回一個數(shù)組趟紊,不包括父類的注解氮双。
一個簡單的例子:
public class Test {
@Retention(RetentionPolicy.RUNTIME)
@interface AnnotationSuper {
}
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface AnnotationSuperWithInherited {
}
@Retention(RetentionPolicy.RUNTIME)
@interface AnnotationChild {
}
@AnnotationSuperWithInherited
@AnnotationSuper
static class FooSuper {
}
@AnnotationChild
static class FooChild extends FooSuper {
}
public static void main(String args[]) {
print(FooChild.class);
print(FooSuper.class);
}
private static void print(Class clx) {
System.out.println("============" + clx.getSimpleName() + "============");
System.out.println("所有:" + Arrays.toString(clx.getAnnotations()));
System.out.println("當前類上:" + Arrays.toString(clx.getDeclaredAnnotations()));
System.out.println("查詢Child:" + clx.getAnnotation(AnnotationChild.class));
System.out.println("查詢Super:" + clx.getAnnotation(AnnotationSuper.class));
System.out.println("查詢WithInherited:" + clx.getAnnotation(AnnotationSuperWithInherited.class));
System.out.println("判斷WithInherited:" + clx.isAnnotationPresent(AnnotationSuperWithInherited.class));
System.out.println("判斷Super:" + clx.isAnnotationPresent(AnnotationSuper.class));
}
}
輸出內容:
============FooChild============
所有:[@net.qiujuer.jumper.annotation.Test$AnnotationSuperWithInherited(), @net.qiujuer.jumper.annotation.Test$AnnotationChild()]
當前類上:[@net.qiujuer.jumper.annotation.Test$AnnotationChild()]
查詢Child:@net.qiujuer.jumper.annotation.Test$AnnotationChild()
查詢Super:null
查詢WithInherited:@net.qiujuer.jumper.annotation.Test$AnnotationSuperWithInherited()
判斷WithInherited:true
判斷Super:false
============FooSuper============
所有:[@net.qiujuer.jumper.annotation.Test$AnnotationSuperWithInherited(), @net.qiujuer.jumper.annotation.Test$AnnotationSuper()]
當前類上:[@net.qiujuer.jumper.annotation.Test$AnnotationSuperWithInherited(), @net.qiujuer.jumper.annotation.Test$AnnotationSuper()]
查詢Child:null
查詢Super:@net.qiujuer.jumper.annotation.Test$AnnotationSuper()
查詢WithInherited:@net.qiujuer.jumper.annotation.Test$AnnotationSuperWithInherited()
判斷WithInherited:true
判斷Super:true
Process finished with exit code 0
至此,Java注解的基礎知識已全部完成霎匈,后續(xù)文章將講解注解在運行時態(tài)的實戰(zhàn)運用和編譯時運用戴差。
尾章
看累了吧?自己反復看一下铛嘱,來一個案例試試~~
END!!!