Java注解 annotation

一.什么是注解

官方解釋:

Java 注解用于為Java程序提供元數(shù)據(jù)篡石。作為元數(shù)據(jù)芥喇,注解不直接影響代碼的執(zhí)行,但也有一些注解實(shí)際上可以用于這一目的夏志。

什么是元數(shù)據(jù)乃坤,即一種描述數(shù)據(jù)的數(shù)據(jù)。所以可以說注解是描述源代碼的數(shù)據(jù)沟蔑。
簡(jiǎn)單理解注解可以看出一個(gè)個(gè)標(biāo)簽湿诊,用來標(biāo)記你的代碼,是一種應(yīng)用于類瘦材,方法厅须,參數(shù),變量食棕,構(gòu)造器及包的一種特殊修飾符朗和。

二.注解的定義

注解和class,interface一樣也是一種類型簿晓,通過@interface定義
如下:

public @interface TestAnnotation {
    
}

三.元注解

注解的應(yīng)用應(yīng)用很簡(jiǎn)單眶拉,我們用使用TestAnnotation注解,在你想注解的類中@TestAnnotation就可以了
如下(注解類):

@TestAnnotation
public class Test {
}

但是要想注解想要正常工作還需要元注解的幫助

1.什么是元注解

元注解就是注解到注解上的注解憔儿,或者說元注解是一種基本注解忆植,它能用來注解其他注解。
我們可以將元注解看成一種特殊的修飾符谒臼,用來解釋說明注解朝刊,它是注解的元數(shù)據(jù)。

2.元注解的種類

元注解一共用5種:

  • @Retention
    Retention意為保留期蜈缤,@Retention用來解釋說明一個(gè)注解的存活周期
    @Retention取值:
    • RetentionPolicy.SOURCE 注解只在源碼階段保留拾氓,在編譯器進(jìn)行編譯時(shí)它將被丟棄忽視。
    • RetentionPolicy.CLASS 注解只被保留到編譯進(jìn)行的時(shí)候底哥,它并不會(huì)被加載到 JVM 中咙鞍。
    • RetentionPolicy.RUNTIME 注解可以保留到程序運(yùn)行的時(shí)候,它會(huì)被加載進(jìn)入到 JVM 中叠艳,所以在程序運(yùn)行時(shí)可以獲取到它們奶陈。
      示例:
@Retention(RetentionPolicy.CLASS)
public @interface TestAnnotation {

}
  • @Documented
    用于描述其它類型的annotation應(yīng)該被作為被標(biāo)注的程序成員的公共API,因此可以被例如javadoc此類的工具文檔化附较。Documented是一個(gè)標(biāo)記注解,沒有成員潦俺。

  • @Target
    指定注解應(yīng)用的地方拒课,用來限定注解的應(yīng)用場(chǎng)景(類徐勃,方法,參數(shù)等等)
    (不使用@Target注解則默認(rèn)不限制)
    取值如下:

    • ElementType.ANNOTATION_TYPE 可以給一個(gè)注解進(jìn)行注解
    • ElementType.CONSTRUCTOR 可以給構(gòu)造方法進(jìn)行注解
    • ElementType.FIELD 可以給屬性進(jìn)行注解
    • ElementType.LOCAL_VARIABLE 可以給局部變量進(jìn)行注解
    • ElementType.METHOD 可以給方法進(jìn)行注解
    • ElementType.PACKAGE 可以給一個(gè)包進(jìn)行注解
    • ElementType.PARAMETER 可以給一個(gè)方法內(nèi)的參數(shù)進(jìn)行注解
    • ElementType.TYPE 可以給一個(gè)類型進(jìn)行注解早像,比如類僻肖、接口、枚舉
  • @Inherited
    繼承的意思卢鹦,當(dāng)一個(gè)超類被@Inherited(@Inherited注解)注解的注解(A注解)進(jìn)行過注解的話臀脏,如果它的子類沒有被如何其他注解進(jìn)行注解,那么這個(gè)子類就繼承了超類的注解(A注解)

    注意:@Inherited annotation類型是被標(biāo)注過的class的子類所繼承冀自。類并不從它所實(shí)現(xiàn)的接口繼承annotation揉稚,方法并不從它所重載的方法繼承annotation

@Retention(RetentionPolicy.CLASS)
@Inherited
public @interface TestAnnotation {
}

@TestAnnotation
public class TestA {
}

public class TestB extends TestA{

}

TestAnnotation 被@Retention注解,類TestA被@TestAnnotation注解熬粗,類TestB繼承類TestA搀玖,類TestB也擁有TestAnnotation注解

  • @Repeatable
    @Repeatable是自然可重復(fù)的意思。這是Java 1.8加進(jìn)來的新特性
    在需要對(duì)同一種注解多次使用時(shí)驻呐,往往需要借助@Repeatable
    舉個(gè)列子
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Roles {
    Role[] value();
}

@Repeatable(Roles.class)
public @interface Role {
    String role() default "";
}

@Role(role="husband")
@Role(role="father")
@Role(role="son")
public class Person {
}

上面的代碼@Repeatable注解了Role 灌诅,@Repeatable 后面括號(hào)中的類相當(dāng)于一個(gè)容器注解

什么是容器注解:
本身也是注解,用來存放其他注解
按照規(guī)定含末,它里面必須要有一個(gè) value 的屬性猜拾,屬性類型是一個(gè)被 @Repeatable 注解過的注解數(shù)組

@Role(role="husband"),role="husband"表示給Role這個(gè)注解的role屬性賦值佣盒,關(guān)于注解的屬性下面會(huì)說明挎袜。
Person 類需要多次使用@Role注解,所以這里使用@Repeatable注解@Role

測(cè)試一下注解效果:

Annotation[] annotations = Person.class.getAnnotations();
        System.out.println(annotations.length);
        Roles p1=(Roles) annotations[0];
        for(Role t:p1.value()){
            System.out.println(t.role());
        }

打印如下:

1
husband
father
son

四.注解的屬性

注解的屬性也叫成員變量沼撕。注解只有成員變量宋雏,沒有方法。注解的成員變量在注解的定義中以“無參的方法”的形式來聲明务豺,其方法名定義了該成員變量的名字磨总,其返回值定義了該成員變量的類型。

  • 注解的屬性類型必須以下幾種:
    8中基本類型(byte笼沥,boolean蚪燕,char,short奔浅,int馆纳,long,float汹桦,double,)和
    String鲁驶,Enum,Class舞骆,annotation類型钥弯,以及這些類型的數(shù)組
public @interface TestAnnotation {
    int id();

    String msg();
}

上面代碼定義TestAnnotation 這個(gè)注解有id和msg兩個(gè)屬性径荔。在使用的時(shí)候我們需要給它們賦值

  • 賦值方式:括號(hào)內(nèi)以value=“”的形式賦值,多個(gè)屬性以'',''隔開
@TestAnnotation(id = 1, msg = "注解測(cè)試")
public class Test {
}
  • 注解中可以設(shè)置默認(rèn)值脆霎,默認(rèn)值用default關(guān)鍵字指定
    使用注解時(shí)對(duì)于指定了默認(rèn)值的屬性总处,如果不需要修改,可以不賦值
public @interface TestAnnotation {
    int id() default 0;

    String msg() default "msg";
}

@TestAnnotation()
public class Test {
}
  • 當(dāng)一個(gè)注解只有一個(gè)屬性且屬性名為value時(shí)睛蛛,使用此注解可以省略括號(hào)內(nèi)的屬性名直接賦值
public @interface TestAnnotation {
    String value();
}

@TestAnnotation("1")
public class Test {
}
  • 如果注解沒有屬性鹦马,括號(hào)也可以省略
public @interface TestAnnotation {
}

@TestAnnotation
public class Test {
}

五.JDK預(yù)制注解

  • @Deprecated
    標(biāo)記過時(shí)元素的注解,用來標(biāo)識(shí)類忆肾,方法或者變量已過時(shí)荸频,不建議使用。調(diào)用過時(shí)方法時(shí)編譯器會(huì)提醒难菌。
  • @Override
    重寫注解试溯,用來表示子類的方法是覆蓋父類的方法。
  • @SuppressWarnings
    抑制警告注解郊酒,用于抑制編譯器產(chǎn)生警告信息.
  • @SafeVarargs
    參數(shù)安全類型注解遇绞。它的目的是提醒開發(fā)者不要用參數(shù)做一些不安全的操作,它的存在會(huì)阻止編譯器產(chǎn)生 unchecked 這樣的警告。它是在 Java 1.7 的版本中加入的
  • @FunctionalInterface
    函數(shù)式接口(函數(shù)式接口可以很容易轉(zhuǎn)換為 Lambda 表達(dá)式)注解燎窘,這個(gè)是 Java 1.8 版本引入的新特性摹闽。函數(shù)式編程很火,所以 Java 8 也及時(shí)添加了這個(gè)特性褐健。

六.注解的提取與應(yīng)用

注解通過反射獲取付鹿,通過Class對(duì)象的方法獲取注解
常用的三個(gè)方法:

  • isAnnotationPresent()方法判斷是否應(yīng)用了某個(gè)注解
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {...}
  • getAnnotation() 方法獲取指定類型的注解
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {...}
  • getAnnotations方法獲取注解到當(dāng)前元素上的所有注解
public Annotation[] getAnnotations() {

示例(以運(yùn)行時(shí)注解為例):獲取下面TestAnnotation 注解的msg屬性內(nèi)容,可以如下獲取

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.FIELD})
public @interface TestAnnotation {
    String msg();
}

@TestAnnotation(msg="注解內(nèi)容")
public class Test {
}

public static void main(String[] args) {
        boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
        if (hasAnnotation){
            TestAnnotation annotation = Test.class.getAnnotation(TestAnnotation.class);
            System.out.println(annotation.msg());
        }
    }

打印結(jié)果:

注解內(nèi)容

方法和屬性也可以借助返回來獲取注解

@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
    String msg();
}

@TestAnnotation(msg="注解類")
public class Test {

    @TestAnnotation(msg="注解成員變量")
    private String msg;

    @TestAnnotation(msg="注解方法")
    private void setMsg(){

    }
}

public static void main(String[] args) {
        boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
        if (hasAnnotation) {
            TestAnnotation annotation = Test.class.getAnnotation(TestAnnotation.class);
            if (annotation != null) {
                System.out.println(annotation.msg());
            }
        }

        try {
            //屬性獲取注解
            Field msg = Test.class.getDeclaredField("msg");
            if (msg != null) {
                msg.setAccessible(true);
                TestAnnotation annotation = msg.getAnnotation(TestAnnotation.class);
                if (annotation != null) {
                    System.out.println(annotation.msg());
                }
            }
            //方法獲取注解
            Method setMsg = Test.class.getDeclaredMethod("setMsg");
            if (setMsg != null){
                TestAnnotation annotation = setMsg.getAnnotation(TestAnnotation.class);
                if (annotation != null) {
                    System.out.println(annotation.msg());
                }
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

打印結(jié)果:

注解類
注解成員變量
注解方法

這里要注意是蚜迅,如果一個(gè)注解要想在運(yùn)行時(shí)被提取舵匾,那么@Retention(RetentionPolicy.RUNTIME)是必須的

注解的應(yīng)用
  • 提供信息給編譯器:編譯器可以通過注解來探測(cè)錯(cuò)誤和警告信息
  • 編譯階段處理:軟件工具可以利用注解信息來自動(dòng)生成代碼,HTML文檔或者做其他相應(yīng)處理
  • 運(yùn)行時(shí)的處理: 某些注解可以在程序運(yùn)行時(shí)接收代碼的提取

當(dāng)開發(fā)者使用注解修飾了類谁不,方法坐梯,變量等成員后,注解不會(huì)自己生效刹帕,必須由開發(fā)者提供對(duì)應(yīng)的代碼來提取處理注解信息吵血。
這些用來提取和處理注解信息的代碼統(tǒng)稱為APT(Annotation Processing Tool),注解處理器偷溺,它用來在編譯時(shí)掃描和處理注解蹋辅。具體可參考APT 基于Android工程
總結(jié)來說,注解(Annotation)相對(duì)于一種標(biāo)記挫掏,注解的應(yīng)用就是編譯器侦另,開發(fā)工具或者程序通過反射來提取你的類和各種元素有無這種標(biāo)記,有某種標(biāo)記就去做相應(yīng)的處理。

參考文章:
秒懂淋肾,Java 注解 (Annotation)你可以這樣學(xué)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末硫麻,一起剝皮案震驚了整個(gè)濱河市爸邢,隨后出現(xiàn)的幾起案子樊卓,更是在濱河造成了極大的恐慌,老刑警劉巖杠河,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碌尔,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡券敌,警方通過查閱死者的電腦和手機(jī)唾戚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來待诅,“玉大人叹坦,你說我怎么就攤上這事”把悖” “怎么了募书?”我有些...
    開封第一講書人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)测蹲。 經(jīng)常有香客問我莹捡,道長(zhǎng),這世上最難降的妖魔是什么扣甲? 我笑而不...
    開封第一講書人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任篮赢,我火速辦了婚禮,結(jié)果婚禮上琉挖,老公的妹妹穿的比我還像新娘启泣。我一直安慰自己,他們只是感情好示辈,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開白布寥茫。 她就那樣靜靜地躺著,像睡著了一般顽耳。 火紅的嫁衣襯著肌膚如雪坠敷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,760評(píng)論 1 289
  • 那天射富,我揣著相機(jī)與錄音膝迎,去河邊找鬼。 笑死胰耗,一個(gè)胖子當(dāng)著我的面吹牛限次,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼卖漫,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼费尽!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起羊始,我...
    開封第一講書人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤旱幼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后突委,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體柏卤,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年匀油,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了缘缚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡敌蚜,死狀恐怖桥滨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情弛车,我是刑警寧澤齐媒,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站帅韧,受9級(jí)特大地震影響里初,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜忽舟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一双妨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧叮阅,春花似錦刁品、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至勒叠,卻和暖如春兜挨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背眯分。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來泰國打工拌汇, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人弊决。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓噪舀,卻偏偏與公主長(zhǎng)得像魁淳,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子与倡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348