簡單的Java注解機制

一拜英、什么是注解

???????注解也叫元數(shù)據(jù)静汤,例如我們常見的@Override和@Deprecated,注解是JDK1.5版本開始引入的一個特性,用于對代碼進行說明虫给,可以對包藤抡、類、接口抹估、字段杰捂、方法參數(shù)、局部變量等進行注解

一般常用的注解可以分為三類:

  • 一類是Java自帶的標準注解棋蚌,包括@Override(標明重寫某個方法)嫁佳、@Deprecated(標明某個類或方法過時)和@SuppressWarnings(標明要忽略的警告),使用這些注解后編譯器就會進行檢查
  • 一類為元注解谷暮,元注解是用于定義注解的注解蒿往,包括@Retention(標明注解被保留的階段)、@Target(標明注解使用的范圍)湿弦、@Inherited(標明注解可繼承)瓤漏、@Documented(標明是否生成javadoc文檔)
  • 一類為自定義注解,可以根據(jù)自己的需求定義注解

二颊埃、注解的用途

  • 生成文檔蔬充,通過代碼里標識的元數(shù)據(jù)生成javadoc文檔。
  • 編譯檢查班利,通過代碼里標識的元數(shù)據(jù)讓編譯器在編譯期間進行檢查驗證饥漫。
  • 編譯時動態(tài)處理,編譯時通過代碼里標識的元數(shù)據(jù)動態(tài)處理罗标,例如動態(tài)生成代碼庸队。
  • 運行時動態(tài)處理,運行時通過代碼里標識的元數(shù)據(jù)動態(tài)處理闯割,例如使用反射注入實例

三彻消、注解的使用

1、Java自帶的標準注解

常見標準的Annotation:

1.)Override
??????? java.lang.Override是一個標記類型注解宙拉,它被用作標注方法宾尚。它說明了被標注的方法重載了父類的方法,起到了斷言的作用谢澈。如果我們使用了這種注解在一個沒有覆蓋父類方法的方法時煌贴,java編譯器將以一個編譯錯誤來警示。

2.)Deprecated
???????Deprecated也是一種標記類型注解澳化。當一個類型或者類型成員使用@Deprecated修飾的話崔步,編譯器將不鼓勵使用這個被標注的程序元素。所以使用這種修飾具有一定的“延續(xù)性”:如果我們在代碼中通過繼承或者覆蓋的方式使用了這個過時的類型或者成員缎谷,雖然繼承或者覆蓋后的類型或者成員并不是被聲明為@Deprecated,但編譯器仍然要報警。

3.)SuppressWarnings
???????SuppressWarning不是一個標記類型注解胧后。它有一個類型為String[]的成員愚墓,這個成員的值為被禁止的警告名音念。對于javac編譯器來講,被-Xlint選項有效的警告名也同樣對@SuppressWarings有效者甲,同時編譯器忽略掉無法識別的警告名。
  @SuppressWarnings("unchecked")

這種注解的使用非常簡單砌创,只需在需要注解的地方標明某個注解即可虏缸,例如在方法上注解:

public class Test {
    @Override
    public String tostring() {
        return "override it";
    }
}

例如在類上注解:

@Deprecated
public class Test {

}

2、元注解

java.lang.annotation提供了四種元注解嫩实,專門注解其他的注解(在自定義注解的時候刽辙,需要使用到元注解):
??????????????@Documented
??????????????@Retention
??????????????@Target
??????????????@Inherited

注解 作用
@Target 表示該注解用于什么地方。如果不明確指出甲献,該注解可以放在任何地方

ElementType.TYPE : 用于描述類宰缤、接口或enum聲明
ElementType.FIELD : 成員變量、對象晃洒、屬性(包括enum實例)
ElementType.METHOD : 用于描述方法
Element.PARAMETER : 用于描述參數(shù)
Element.CONSTRUCTOR : 用于描述構(gòu)造器
Element.LOCAL_VARIABLE : 用于描述局部變量
ElementType.ANNOTATION_TYPE : 用于描述類慨灭、接口(包括注解類型) 或enum聲明
ElementType.PACKAGE : 用于描述包
@Retention 定義該注解的生命周期

RetentionPolicy.SOURCE : 在編譯階段丟棄。這些注解在編譯結(jié)束之后就不再有任何意義球及,所以它們不會寫入字節(jié)碼(@Override, @SuppressWarnings都屬于這類注解)
RetentionPolicy.CLASS : 在類加載的時候丟棄氧骤。在字節(jié)碼文件的處理中有用。注解默認使用這種方式
RetentionPolicy.RUNTIME : 始終不會丟棄吃引,運行期也保留該注解语淘,因此可以使用反射機制讀取該注解的信息。我們自定義的注解通常使用這種方式
@Document 表示是否將注解信息添加在java文檔中
@Inherited 定義該注釋和子類的關(guān)系

注:如果一個使用了@Inherited修飾的annotation類型被用于一個class际歼,則這個annotation將被用于該class的子類惶翻。@Inherited annotation類型是被標注過的class的子類所繼承。類并不從它所實現(xiàn)的接口繼承annotation鹅心,方法并不從它所重載的方法繼承annotation

3吕粗、自定義注解

自定義注解類編寫的一些規(guī)則:

  1. Annotation型定義為@interface, 所有的Annotation會自動繼承java.lang.Annotation這一接口,并且不能再去繼承別的類或是接口.
  2. 參數(shù)成員只能用public或默認(default)這兩個訪問權(quán)修飾
  3. 參數(shù)成員只能用基本類型byte,short,char,int,long,float,double,boolean八種基本數(shù)據(jù)類型和String、Enum旭愧、Class颅筋、annotations等數(shù)據(jù)類型,以及這一些類型的數(shù)組.
  4. 要獲取類方法和字段的注解信息,必須通過Java的反射技術(shù)來獲取 Annotation對象,因為你除此之外沒有別的獲取注解對象的方法
  5. 注解也可以沒有定義成員, 不過這樣注解就沒啥用了
    PS:自定義注解需要使用到元注解
 @Target(ElementType.METHOD)
 @Retention(RetentionPolicy.RUNTIME)
 public @interface Test {
   
 }

???????在注解中一般會有一些元素以表示某些值输枯。注解的元素看起來就像接口的方法议泵,唯一的區(qū)別在于可以為其制定默認值。沒有元素的注解稱為標記注解桃熄,上面的@Test就是一個標記注解先口。
???????注解的可用的類型包括以下幾種:所有基本類型、String、Class碉京、enum厢汹、Annotation、以上類型的數(shù)組形式谐宙。元素不能有不確定的值烫葬,即要么有默認值,要么在使用注解的時候提供元素的值凡蜻。而且元素不能使用null作為默認值搭综。注解在只有一個元素且該元素的名稱是value的情況下,在使用注解的時候可以省略“value=”划栓,直接寫需要的值即可兑巾。

四、反射機制與注解機制

AnnotatedElement 接口是所有程序元素(Class茅姜、Method和Constructor)的父接口闪朱,所以程序通過反射獲取了某個類的AnnotatedElement對象之后,程序就可以調(diào)用該對象的如下四個個方法來訪問Annotation信息:

方法 含義
<T extends Annotation> T getAnnotation(Class<T> annotationClass) 返回改程序元素上存在的钻洒、指定類型的注解奋姿,如果該類型注解不存在,則返回null
Annotation[] getAnnotations() 返回該程序元素上存在的所有注解素标。
boolean is AnnotationPresent(Class<?extends Annotation> annotationClass) 判斷該程序元素上是否包含指定類型的注解称诗,存在則返回true,否則返回false
Annotation[] getDeclaredAnnotations() 返回直接存在于此元素上的所有注解头遭。與此接口中的其他方法不同寓免,該方法將忽略繼承的注解。(如果沒有注釋直接存在于此元素上计维,則返回長度為零的一個數(shù)組袜香。)該方法的調(diào)用者可以隨意修改返回的數(shù)組;這不會對其他調(diào)用者返回的數(shù)組產(chǎn)生任何影響鲫惶。
default <T extends Annotation> T[] getAnnotationsByType(Class <T> annotationClass) 返回該元素制定類型的注解
default <T extends Annotation> T getDeclaredAnnotation( Class <T> annotationClass) 返回直接存在與該元素上的所有注解
default <T extends Annotation>T[] getDeclaredAnntationsByType(Class <T> annotationClass) 返回直接存在該元素上某類型的注解

接下來讓我們在代碼中看看二者的結(jié)合

//定義一個可以注解在Class,interface,enum上的注解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnTargetType {
    /**
     * 定義注解的一個元素 并給定默認值
     * @return
     */
    String value() default "MyAnTargetType我是定義在類接口枚舉類上的注解元素value的默認值";
}
//定義一個可以注解在PARAMETER上的注解

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnTargetParameter {
    /**
     * 定義注解的一個元素 并給定默認值
     * @return
     */
    String value() default "MyAnTargetParameter我是定義在參數(shù)上的注解元素value的默認值";
}
// 定義一個可以注解在METHOD上的注解

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnTargetMethod {
    /**
     * 定義注解的一個元素 并給定默認值
     *
     * @return
     */
    String value() default "MyAnTargetMethod我是定義在方法上的注解元素value的默認值";
}
// 定義一個可以注解在FIELD上的注解

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnTargetField {
    /**
     * 定義注解的一個元素 并給定默認值
     * @return
     */
    String value() default "MyAnTargetField我是定義在字段上的注解元素value的默認值";
}
//創(chuàng)建一個使用了以上四種注解的類

@MyAnTargetType
public class AnnotationTest {
    @MyAnTargetField
    private String field = "我是字段";

    @MyAnTargetMethod("測試方法")
    public void test(@MyAnTargetParameter String args) {
        System.out.println("參數(shù)值 === " + args);
    }
}
public class Main{

    public static void main(String[] args) {
        // 獲取類上的注解MyAnTargetType
        MyAnTargetType t = AnnotationTest.class.getAnnotation(MyAnTargetType.class);
        System.out.println("類上的注解值 === " + t.value());
        MyAnTargetMethod tm = null;
        try {
            // 根據(jù)反射獲取AnnotationTest類上的test方法
            Method method = AnnotationTest.class.getDeclaredMethod("test", String.class);
            // 獲取方法上的注解MyAnTargetMethod
            tm = method.getAnnotation(MyAnTargetMethod.class);
            System.out.println("方法上的注解值 === " + tm.value());
            // 獲取方法上的所有參數(shù)注解  循環(huán)所有注解找到MyAnTargetParameter注解
            Annotation[][] annotations = method.getParameterAnnotations();

            for (Annotation[] tt : annotations) {
                for (Annotation t1 : tt) {
                    if (t1 instanceof MyAnTargetParameter) {
                        System.out.println("參數(shù)上的注解值 === " + ((MyAnTargetParameter) t1).value());
                    }
                }
            }

            method.invoke(new AnnotationTest(), "改變默認參數(shù)");
            // 獲取AnnotationTest類上字段field的注解MyAnTargetField
            MyAnTargetField fieldAn = AnnotationTest.class.getDeclaredField("field").getAnnotation(MyAnTargetField.class);
            System.out.println("字段上的注解值 === " + fieldAn.value());
        } catch (Exception e) {
            e.printStackTrace();
        }

運行結(jié)果如下:

類上的注解值 === MyAnTargetType我是定義在類接口枚舉類上的注解元素value的默認值
方法上的注解值 === 測試方法
參數(shù)上的注解值 === MyAnTargetParameter我是定義在參數(shù)上的注解元素value的默認值
參數(shù)值 === 改變默認參數(shù)
字段上的注解值 === MyAnTargetField我是定義在字段上的注解元素value的默認值

最后再寫一個關(guān)于@Inherited例子:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MyAnTargetType {
    /**
     * 定義注解的一個元素 并給定默認值
     * @return
     */
    String value() default "MyAnTargetType我是定義在類接口枚舉類上的注解元素value的默認值";
}
public class Main extends AnnotationTest{

    public static void main(String[] args) {
        MyAnTargetType t2 = Main.class.getAnnotation(MyAnTargetType.class);
        System.out.println("類上的注解值 === "+t2.value());
    }
}

運行如下:

類上的注解值 === 我是定義在類接口枚舉類上的注解元素value的默認值

說明已經(jīng)獲取到了父類AnnotationTest的注解了
如果MyAnTargetType去掉@Inherited注解運行則報錯如下:

Exception in thread "main" java.lang.NullPointerException
    at com.company.Main.main(Main.java:41)

最后借用下別人的Java注解的基礎(chǔ)知識點導圖


5713484-e2afe3f5096f32e9.png

關(guān)于Java注解機制的基礎(chǔ)就講完了蜈首,歡迎大家前來diss

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市欠母,隨后出現(xiàn)的幾起案子欢策,更是在濱河造成了極大的恐慌,老刑警劉巖赏淌,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件踩寇,死亡現(xiàn)場離奇詭異,居然都是意外死亡六水,警方通過查閱死者的電腦和手機俺孙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進店門辣卒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人鼠冕,你說我怎么就攤上這事添寺】瓒ⅲ” “怎么了懈费?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長博脑。 經(jīng)常有香客問我憎乙,道長,這世上最難降的妖魔是什么叉趣? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任泞边,我火速辦了婚禮,結(jié)果婚禮上疗杉,老公的妹妹穿的比我還像新娘阵谚。我一直安慰自己,他們只是感情好烟具,可當我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布梢什。 她就那樣靜靜地躺著,像睡著了一般朝聋。 火紅的嫁衣襯著肌膚如雪嗡午。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天冀痕,我揣著相機與錄音荔睹,去河邊找鬼。 笑死言蛇,一個胖子當著我的面吹牛僻他,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播腊尚,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼吨拗,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了跟伏?” 一聲冷哼從身側(cè)響起丢胚,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎受扳,沒想到半個月后携龟,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡勘高,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年峡蟋,在試婚紗的時候發(fā)現(xiàn)自己被綠了坟桅。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蕊蝗,死狀恐怖仅乓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蓬戚,我是刑警寧澤夸楣,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站紧显,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏缕棵。R本人自食惡果不足惜篙程,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧折柠,春花似錦、人聲如沸承冰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽昌粤。三九已至岸更,卻和暖如春谭企,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背盹廷。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工缸榄, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鹰贵。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓腊瑟,卻偏偏與公主長得像,于是被迫代替她去往敵國和親瘪贱。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,884評論 2 354

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

  • 什么是注解(Annotation):Annotation(注解)就是Java提供了一種元程序中的元素關(guān)聯(lián)任何信息和...
    九尾喵的薛定諤閱讀 3,166評論 0 2
  • 對于java中的思考的方向共螺,1必須要看前端的頁面,對于前端的頁面基本的邏輯情竹,如果能理解最好藐不,不理解也要知道幾點雏蛮。 ...
    神尤魯?shù)婪?/span>閱讀 814評論 0 0
  • 本文章涉及代碼已放到github上annotation-study 1.Annotation為何而來 What:A...
    zlcook閱讀 29,158評論 15 116
  • 整體Retrofit內(nèi)容如下: 1贡耽、Retrofit解析1之前哨站——理解RESTful 2滥嘴、Retrofit解析...
    隔壁老李頭閱讀 6,479評論 4 31
  • 51.養(yǎng)成自己查看各項政策通知更新的習慣,拓寬自己的信息渠道晦譬。 52.合理規(guī)劃自己的支出收入疤苹,做一個有經(jīng)濟計劃的人...
    我叫何奕洺閱讀 178評論 0 0