Android注解知識點梳理-基礎理論

目的

學完本文章你將會明白什么是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基礎的時候提供的棚赔;那么何為元注解?

“用于注解其他注解的注解叫做元注解徘郭∫涫龋” 他的本質也是注解,只不過相對特殊是用于注解其他的注解而已崎岂。

  1. @Documented:注解是否將包含在JavaDoc中
  2. @Retention:什么時候使用該注解
  3. @Target:注解用于什么地方
  4. @Inherited:是否允許子類繼承該注解
  5. @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" 生成文檔。

可以發(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_PARAMETERTYPE_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!!!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末暖释,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子墨吓,更是在濱河造成了極大的恐慌球匕,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件帖烘,死亡現(xiàn)場離奇詭異,居然都是意外死亡秘症,警方通過查閱死者的電腦和手機照卦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來乡摹,“玉大人役耕,你說我怎么就攤上這事√诵叮” “怎么了蹄葱?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵氏义,是天一觀的道長锄列。 經常有香客問我,道長惯悠,這世上最難降的妖魔是什么邻邮? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮克婶,結果婚禮上筒严,老公的妹妹穿的比我還像新娘。我一直安慰自己情萤,他們只是感情好鸭蛙,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著筋岛,像睡著了一般娶视。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上肪获,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天较木,我揣著相機與錄音,去河邊找鬼泳赋。 笑死祖今,一個胖子當著我的面吹牛,可吹牛的內容都是我干的徐绑。 我是一名探鬼主播傲茄,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼草巡!你這毒婦竟也來了?” 一聲冷哼從身側響起郁竟,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蔑舞,沒想到半個月后攻询,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體低零,經...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了老厌。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖薇溃,靈堂內的尸體忽然破棺而出痊焊,到底是詐尸還是另有隱情,我是刑警寧澤逛尚,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站觉壶,受9級特大地震影響件缸,放射性物質發(fā)生泄漏争剿。R本人自食惡果不足惜蚕苇,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧锰茉,春花似錦飒筑、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至削饵,卻和暖如春窿撬,著一層夾襖步出監(jiān)牢的瞬間箫津,已是汗流浹背苏遥。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留教硫,地道東北人瞬矩。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓惭蹂,卻偏偏與公主長得像媚污,于是被迫代替她去往敵國和親廷雅。 傳聞我的和親對象是個殘疾皇子耗美,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內容