Java 注解 Annotation

What

Annotation 是何物 ? ** @Override **相信 Java 程序員都不陌生织阳,經(jīng)常會在一些方法上看到這樣一個符號仿贬,這個 ** @Override **便是一個Annotation。如:

    @Override
    public String toString() {
        return super.toString();
    }

Annotation:中文譯之為注解,是一種能夠添加到 Java 源代碼中的語法元數(shù)據(jù),于 Java SE5中被引入卖鲤。注解可用來將信息元數(shù)據(jù)與程序元素進(jìn)行關(guān)聯(lián),可以作用于包畴嘶、類蛋逾、接口、方法窗悯、域区匣、參數(shù)、注解之上蟀瞧。注解為我們在代碼中添加信息提供了一種形式化的方法沉颂,使我們可以在稍后某個時刻非常方便的使用這些數(shù)據(jù)。

Why

注解在一定程度上是在將元數(shù)據(jù)與源代碼文件結(jié)合在一起悦污,而不是保存在外部文檔中的大趨勢下誕生的铸屉。注解的優(yōu)點(diǎn)主要有:

  • 注解可以提供用來完整地描述程序所需要的信息;
  • 注解可以將一些數(shù)據(jù)切端、格式的驗證和測試工作抽取出來交由編譯器彻坛;
  • 注解可以用來生成描述符文件、新的類定義文件或者一些“樣板”代碼踏枣,有助于減輕編寫“樣板”代碼的負(fù)擔(dān)昌屉;
  • 使用注解從某種程度上可以提高代碼的可讀性;

注解分類

元注解:即專職用于創(chuàng)建其它注解的注解茵瀑,Java提供了四種元注解:@Target间驮、@Retention、@Documented 和 @ Inherited

@Target:該注解用于定義注解的作用域马昨,值可以取枚舉類型 ElementType 中的一個或多個竞帽,如果未指定Target值,則該注解可以作用于如下所有的域鸿捧。

  • TYPE:類/接口/枚舉/Annotation 聲明
  • FIELD:域/枚舉常量聲明
  • METHOD:方法聲明
  • PARAMETER:參數(shù)聲明
  • CONSTRUCTOR:構(gòu)造函數(shù)聲明
  • LOCAL_VARIABLE:局部變量聲明
  • ANNOTATION_TYPE:Annotation 類型聲明
  • PACKAGE:包聲明

@Retention:該注解表示需要在什么級別保存該注解信息屹篓,可選值為枚舉類型 RetentionPolicy 中的一個:

  • SOURCE:注解將被編譯器丟棄,主要起** 標(biāo)記 **作用匙奴,告訴編譯器一些信息堆巧,如@Override 和 @SuppressWarnings;
  • CLASS:注解在 class 文件中可用,但是會被 JVM 丟棄谍肤,可用于編譯時動態(tài)處理啦租,如動態(tài)生成代碼;
  • RUNTIME:注解將會一直保留荒揣,因此可以通過反射機(jī)制讀取注解信息刷钢,@Deprecated 和 @SafeVarargs;

@Documented :將此注解保留在 Javadoc 中乳附;
@Inherited:允許子類繼承父類中的注解。

標(biāo)準(zhǔn)注解:Java內(nèi)置了四種標(biāo)準(zhǔn)注解:@Override伴澄、@SuppressWarnings赋除、@Deprecated 和 @SafeVarargs
@Override:表示當(dāng)前的方法定義將會覆蓋父類中的方法,如果當(dāng)前方法簽名與父類中的方法簽名不一致非凌,編譯器將會發(fā)出錯誤提示信息举农;
@SuppressWarnings:關(guān)閉不當(dāng)?shù)木幾g器警告信息;
@Deprecated :表示該方法已經(jīng)被棄用敞嗡,如果程序中使用到被該注解作用的方法颁糟,編譯器會發(fā)出警告信息;
@SafeVarargs:該注解是 Java 1.7 新引入的一個注解喉悴,表示該注解作用的對象不會對其可變參數(shù)進(jìn)行任何潛在不安全的修改

自定義注解:Java提供的注解數(shù)目相對比較少棱貌,不過我們可以通過自定義注解的方式創(chuàng)建符合自己程序需要的注解類型。

How

這部分主要介紹如何創(chuàng)建一個新的注解并根據(jù)程序需要獲取注解信息進(jìn)行處理箕肃。

定義注解

自定義注解之前婚脱,先看看 Java 標(biāo)準(zhǔn)注解是怎樣定義的,從而對注解有個大致印象勺像!
下面兩段代碼分別是 @Override 和 @SuppressWarnings 注解的定義障贸。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)    //使用元注解設(shè)置注解作用域、保留策略等
public @interface Override {
}
@Target( { ElementType.TYPE, ElementType.FIELD, ElementType.METHOD,
        ElementType.PARAMETER, ElementType.CONSTRUCTOR,
        ElementType.LOCAL_VARIABLE })
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    public String[] value();      // 注解配置信息
}

從上面兩個標(biāo)準(zhǔn)注解定義中可以看出吟宦,注解的定義有點(diǎn)類似于接口定義篮洁,注解也會被編譯成 .class 文件。

** 定義注解的步驟:**

  1. 使用** @interface ** 就可以定義一個注解殃姓,注解名緊隨其后袁波;
  2. 然后根據(jù)程序需要,使用 Java 元注解設(shè)置注解的作用域辰狡、保留級別锋叨、是否可繼承等信息;
  3. 根據(jù)需要宛篇,在注解中包含一些元素用于表示一些配置參數(shù)娃磺,提供必要的注解信息以便于后續(xù)對注解進(jìn)行分析處理。

** 實例 **

@Target(ElementType.METHOD)              // 該注解可以作用于方法聲明
@Retention(RetentionPolicy.RUNTIME)     // 該注解會一直保留到運(yùn)行時
@Documented                            // 將注解信息保留到 Javadoc中               
@Inherited                            // 該注解可以被子類繼承
public @interface Property {
     int id();                          // int 類型元素 id
     String msg() default "default msg";  // 默認(rèn)值為 “default msg” 的 String 類型元素 msg
//   Boolean flag() default false;    // Invalide type 'Boolean' for annotaton member
}

上面一段代碼自定義了一個名為 Property 的注解叫倍,該注解可以作用于方法聲明偷卧,內(nèi)部包含兩個配置參數(shù) id 和 msg豺瘤,使用注解時可通過這兩個元素傳遞注解信息。

注解元素

注解中包含的方法聲明即為注解元素听诸,注解元素定義類似于接口中的方法定義坐求,元素的類型即為所需參數(shù)的類型,如 int id()表示需要一個int類型值晌梨,不過注解元素可以通過使用default關(guān)鍵字定義默認(rèn)值桥嗤,如果在使用注解時,沒有為設(shè)置了默認(rèn)值的元素提供值仔蝌,則會使用該元素的默認(rèn)值泛领。不包含注解元素的注解為** 標(biāo)記注解 **,這類注解的存在只是為了提供某種標(biāo)記信息敛惊,如@Override注解渊鞋。

注解元素只能使用如下幾種類型,使用其余類型時瞧挤,編譯器會給出錯誤提示:

  • 所有基本類型(int锡宋、char、double等)
  • String
  • Class
  • enum
  • Annotation(即嵌套注解)
  • 以上類型的數(shù)組(注意只能是數(shù)組特恬,而不能是容器類型)

使用注解

public class Goods {

    @Property(id = 1, msg = "per price")
    public void price(){}

    @Property(id = 2)
    public void type(){}
}

注解通過名值對的形式賦值执俩,具有默認(rèn)值的注解可以不賦值,從而使用默認(rèn)值癌刽。上面這段代碼在 Goods 類中創(chuàng)建了兩個函數(shù) price() 和 type()奠滑,這兩個函數(shù)均使用了自定義的 Property 注解,price() 函數(shù)為配置參數(shù) id 和 msg 都賦了值妒穴,而type()函數(shù)只設(shè)置了 id 的值宋税,這意味著type()函數(shù)的 msg 為默認(rèn)值“default msg”。

編寫注解處理器

至此已經(jīng)了解了如何創(chuàng)建和使用注解讼油,然而如果沒有注解處理器對注解信息進(jìn)行加工處理的話杰赛,注解也就不能顯示其強(qiáng)大功能了。Java SE5擴(kuò)展了反射機(jī)制的 API矮台,以幫助程序員構(gòu)建這類工具乏屯。同時,還提供了一個外部工具 apt(Annotation Process Tool)幫助解析帶有注解的 Java源碼瘦赫。所有注解均可以使用Mirror API 在編譯時便獲取類型信息辰晕,但只有@Retention 為 RetentionPolicy.RUNTIME 的注解可以在運(yùn)行時使用Java反射機(jī)制獲取數(shù)據(jù)。

下面代碼是一個簡單的注解處理器确虱,用于讀取上面的 Goods 類含友,并使用反射機(jī)制在運(yùn)行時查找我們自定義的注解Property,通過Property注解獲取方法的 id 和 msg。

public class PropertyTracker {

    public static void trackerProperty(List<Integer> ids, Class cls){
        for (Method method : cls.getDeclaredMethods()){
            Property annot = method.getAnnotation(Property.class);
            if (annot != null){
                print("The Annotation's id is: " + annot.id() + " msg is: " + annot.msg());
                ids.remove(new Integer(annot.id()));
            }else {
                print("This method doesn't has Annotation - Property!");
            }
        }

        for (Integer i : ids){
            print("Warning! Missing Property - " + i);
        }
    }

    public static void main(String[] args) {
        List<Integer> propertys = new ArrayList<>(5);
        Collections.addAll(propertys, 1, 2, 3, 4, 5);
        trackerProperty(propertys, Goods.class);
    }
}
//Output:
The Annotation's id is: 2 msg is: default msg
The Annotation's id is: 1 msg is: per price
Warning! Missing Property - 3
Warning! Missing Property - 4
Warning! Missing Property - 5

解析:
trackerProperty方法需要一個List 類型參數(shù) ids 和 一個Class類型參數(shù)cls窘问,然后它會找出cls中有的Property和缺失的Property辆童。
trackerProperty方法中使用到了兩個反射方法:
getDeclaredMethods():返回一個包含該類中定義的所有方法的數(shù)組
getAnnotation():返回實參指定類型的Annotation,如果調(diào)用者沒有該類型的注解則返回null值惠赫。
通過使用注解對象 annot 的id()和msg()方法獲取注解的id和msg信息把鉴。

快速賦值機(jī)制

當(dāng)注解內(nèi)部只有一個需要賦值的注解元素時,可以將該注解元素名設(shè)為value儿咱,從而提供一種快捷賦值方式庭砍。即使用該注解時,直接在括號內(nèi)提供注解元素的值即可混埠,而無需使用名值對的方式逗威。
如下所示:

@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Person {
    int value();
    String sex() default "girl";
    boolean student() default true;
}
public class Chatlist {

    @Person(1)
    String per1 = "person1";

    @Person(value = 2, sex = "boy", student = false)
    String per2 = "person2";
}

注解Person內(nèi)部包含三個注解元素:value、sex和student岔冀,其中sex和student均有默認(rèn)值,所以value是唯一需要賦值的注解元素概耻,可以使用快速賦值機(jī)制使套。不過當(dāng)需要賦值的元素不止一個時,則value也需要使用名值對的方式賦值鞠柄,如per2所示侦高。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市厌杜,隨后出現(xiàn)的幾起案子奉呛,更是在濱河造成了極大的恐慌,老刑警劉巖夯尽,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瞧壮,死亡現(xiàn)場離奇詭異,居然都是意外死亡匙握,警方通過查閱死者的電腦和手機(jī)咆槽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來圈纺,“玉大人秦忿,你說我怎么就攤上這事《耆ⅲ” “怎么了灯谣?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蛔琅。 經(jīng)常有香客問我胎许,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任呐萨,我火速辦了婚禮杀饵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘谬擦。我一直安慰自己切距,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布惨远。 她就那樣靜靜地躺著谜悟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪北秽。 梳的紋絲不亂的頭發(fā)上葡幸,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天,我揣著相機(jī)與錄音贺氓,去河邊找鬼蔚叨。 笑死,一個胖子當(dāng)著我的面吹牛辙培,可吹牛的內(nèi)容都是我干的蔑水。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼扬蕊,長吁一口氣:“原來是場噩夢啊……” “哼搀别!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起尾抑,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤歇父,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后再愈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體榜苫,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年翎冲,在試婚紗的時候發(fā)現(xiàn)自己被綠了单刁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡府适,死狀恐怖羔飞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情檐春,我是刑警寧澤逻淌,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站疟暖,受9級特大地震影響卡儒,放射性物質(zhì)發(fā)生泄漏田柔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一骨望、第九天 我趴在偏房一處隱蔽的房頂上張望硬爆。 院中可真熱鬧,春花似錦擎鸠、人聲如沸缀磕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽袜蚕。三九已至,卻和暖如春绢涡,著一層夾襖步出監(jiān)牢的瞬間牲剃,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工雄可, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留凿傅,地道東北人。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓数苫,卻偏偏與公主長得像聪舒,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子文判,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評論 2 361

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