1.8 Java 注解annotation

1.1 注解聲明

Java注解Annotation,有聲明注解和元注解

  • 元注解:Java提供的元注解,所謂元注解就是標(biāo)記其他注解的注解(@Target,@Retention),即@Target(ElementType.ANNOTATION_TYPE)
  • 聲明注解:用@interface聲明的注解

@Target

@Target 用來約束注解可以應(yīng)用的地方(如類,方法,字段)

/*
 * @since 1.5
 * @jls 9.6.4.1 @Target
 * @jls 9.7.4 Where Annotations May Appear
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
    ElementType[] value();
}
public enum ElementType {
    /** 標(biāo)明該注解可以用于類、接口(包括注解類型)或enum聲明 */
    TYPE,

    /** 標(biāo)明該注解可以用于字段(域)聲明,包括enum實(shí)例 */
    FIELD,

    /** 標(biāo)明該注解可以用于方法聲明 */
    METHOD,

    /** 標(biāo)明該注解可以用于參數(shù)聲明 */
    PARAMETER,

    /** 標(biāo)明注解可以用于構(gòu)造函數(shù)聲明 */
    CONSTRUCTOR,

    /** 標(biāo)明注解可以用于局部變量聲明 */
    LOCAL_VARIABLE,

    /** 標(biāo)明注解可以用于注解聲明(應(yīng)用于另一個(gè)注解上)*/
    ANNOTATION_TYPE,

    /** 標(biāo)明注解可以用于包聲明 */
    PACKAGE,

    /**
     * 標(biāo)明注解可以用于類型參數(shù)聲明(1.8新加入)
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * 類型使用聲明(1.8新加入)
     * @since 1.8
     */
    TYPE_USE
}

當(dāng)聲明的注解未指定Target值時(shí),則此注解可以用于任何元素之上,多個(gè)值使用{}包含并用逗號(hào)隔開,如下:

@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})

@Retention

用來約束注解的保留的地方,有三個(gè)值,源碼級(jí)別(source),類文件級(jí)別(class)或者運(yùn)行時(shí)級(jí)別(runtime)

/*
 * @author  Joshua Bloch
 * @since 1.5
 * @jls 9.6.3.2 @Retention
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}
public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}

1.2 注解元素

如何聲明一個(gè)注解元素:

@Target(ElementType.TYPE)//只能應(yīng)用于類上
@Retention(RetentionPolicy.RUNTIME)//保存到運(yùn)行時(shí)
public @interface DBTable {
    String name() default "";
}

上面這個(gè)例子說明,DBTable這個(gè)注解聲明了一個(gè)String類型的name的元素。

default關(guān)鍵字 表示默認(rèn)值

name() 方法不能傳參,否則編譯出錯(cuò):

@interface members may not have parameter

在使用注解元素的時(shí)候,使用鍵值對(duì)的方式,以方法名作為key,value表示元素的返回值

//在類上使用該注解
@DBTable(name = "MEMBER")
public class Member {
    //.......
}

注解支持的元素?cái)?shù)據(jù)類型除了String,還支持如下數(shù)據(jù)類型:

  • 所有基本類型(使用基本類型但不允許使用任何包裝類型)
  • String
  • Class
  • enum
  • Annotation
  • 上面類型的數(shù)組

示例:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Reference{
    boolean next() default false;
}

public @interface AnnotationElementDemo {
    //枚舉類型
    enum Status {FIXED,NORMAL};

    //聲明枚舉
    Status status() default Status.FIXED;

    //布爾類型
    boolean showSupport() default false;

    //String類型
    String name()default "";

    //class類型
    Class<?> testCase() default Void.class;

    //注解嵌套
    Reference reference() default @Reference(next=true);

    //數(shù)組類型
    long[] value();
}

編譯器對(duì)元素的默認(rèn)值有些過分挑剔。

  • 元素必須要有值:要么定義元素的時(shí)候設(shè)置默認(rèn)值,要么在使用注解的時(shí)候提供元素的值
  • 元素的值不能為null。

1.3 注解不支持繼承

注解是不支持繼承的,因此不能使用關(guān)鍵字extends來繼承某個(gè)@interface,但注解在編譯后,編譯器會(huì)自動(dòng)繼承java.lang.annotation.Annotation接口,這里我們反編譯前面定義的DBTable注解:

import java.lang.annotation.Annotation;
//反編譯后的代碼
public interface DBTable extends Annotation
{
    public abstract String name();
}

雖然反編譯后發(fā)現(xiàn)DBTable注解繼承了Annotation接口,請(qǐng)記住,即使Java的接口可以實(shí)現(xiàn)多繼承,但定義注解時(shí)依然無法使用extends關(guān)鍵字繼承@interface。

1.4 注解快捷方式

快捷方式就是注解中定義了名為value的元素,并且在使用該注解時(shí),如果該元素是唯一需要賦值的一個(gè)元素,那么此時(shí)無需使用key=value的語(yǔ)法,而只需在括號(hào)內(nèi)給出value元素所需的值即可。這可以應(yīng)用于任何合法類型的元素,記住,這限制了元素名必須為value,簡(jiǎn)單案例如下:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface IntegerVaule{
    int value() default 0;
    String name() default "";
}

//使用注解
public class QuicklyWay {

    //當(dāng)只想給value賦值時(shí),可以使用以下快捷方式
    @IntegerVaule(20)
    public int age;

    //當(dāng)name也需要賦值時(shí)必須采用key=value的方式賦值
    @IntegerVaule(value = 10000,name = "MONEY")
    public int money;

}

2.1 Java內(nèi)置注解與其它元注解

Java常用內(nèi)置注解三個(gè):
  • @Override:用于標(biāo)明此方法覆蓋了父類的方法
  • @Deprecated:用于標(biāo)明已經(jīng)過時(shí)的方法或類
  • @SuppressWarnnings:用于有選擇的關(guān)閉編譯器對(duì)類、方法、成員變量、變量初始化的警告.其內(nèi)部有一個(gè)String數(shù)組,主要接收值如下:
    • deprecation:使用了不贊成使用的類或方法時(shí)的警告;
    • unchecked:執(zhí)行了未檢查的轉(zhuǎn)換時(shí)的警告,例如當(dāng)使用集合時(shí)沒有用泛型 (Generics) 來指定集合保存的類型;
    • fallthrough:當(dāng) Switch 程序塊直接通往下一種情況而沒有 Break 時(shí)的警告;
    • path:在類路徑、源文件路徑等中有不存在的路徑時(shí)的警告;
    • serial:當(dāng)在可序列化的類上缺少 serialVersionUID 定義時(shí)的警告;
    • finally:任何 finally 子句不能正常完成時(shí)的警告;
    • all:關(guān)于以上所有情況的警告。
Java常用內(nèi)置元注解:
  • @Target
  • @Retention
  • @Documented 被修飾的注解會(huì)生成到j(luò)avadoc中
  • @Inherited 可以讓注解被繼承,但這并不是真的繼承,只是通過使用@Inherited,可以讓子類Class對(duì)象使用getAnnotations()獲取父類被@Inherited修飾的注解

3. 注解與反射機(jī)制

經(jīng)過反編譯后, 知道Java所有注解都繼承了Annotation接口。也就是說 Java使用Annotation接口代表注解元素。
為了運(yùn)行時(shí)能準(zhǔn)確獲取到注解的相關(guān)信息,Java在java.lang.reflect 反射包下新增了AnnotatedElement接口,它主要用于表示目前正在 VM 中運(yùn)行的程序中已使用注解的元素,通過該接口提供的方法可以利用反射技術(shù)地讀取注解的信息,如反射包的Constructor類、Field類、Method類、Package類和Class類都實(shí)現(xiàn)了AnnotatedElement接口

示例:

注解類:DocumentA

import java.lang.annotation.*;

@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DocumentA {
}

被注解修飾類A:

@DocumentA
public class A {
}

測(cè)試類DocumentDemo:

public class DocumentDemo extends  A{


    public static void main(String[] args) {
        Class<?> clazz = DocumentDemo.class;
        //根據(jù)指定注解類型獲取該注解
        DocumentA documentA = clazz.getAnnotation(DocumentA.class);
        System.out.println("A:"+documentA);

        //獲取該元素上的所有注解,包含從父類繼承
        Annotation[] annotations = clazz.getAnnotations();
        System.out.println("an:"+ Arrays.toString(annotations));

        //獲取該元素上的所有注解,但不包含繼承!
        Annotation[] an2 = clazz.getDeclaredAnnotations();
        System.out.println("an2:"+ Arrays.toString(an2));

        //判斷注解DocumentA是否在該元素上
        boolean b=clazz.isAnnotationPresent(DocumentA.class);
        System.out.println("b:"+b);

    }

}


-----------------

輸入結(jié)果:
A:@com.littlezan.test.testannotation.DocumentA()
an:[@com.littlezan.test.testannotation.DocumentA()]
an2:[]
b:true

4. 運(yùn)行時(shí)注解處理器

通過注解Api和反射包中與注解相關(guān)的Api,解析注解內(nèi)容來組裝需要的數(shù)據(jù)

5. Java 8中注解增強(qiáng)

5.1 元注解@Repeatable

元注解@Repeatable是JDK1.8新加入的,它表示在同一個(gè)位置重復(fù)相同的注解。在沒有該注解前,一般是無法在同一個(gè)類型上使用相同的注解的

//Java8前無法這樣使用
@FilterPath("/web/update")
@FilterPath("/web/add")
public class A {}

Java8前如果是想實(shí)現(xiàn)類似的功能,我們需要在定義@FilterPath注解時(shí)定義一個(gè)數(shù)組元素接收多個(gè)值如下

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface FilterPath {
    String [] value();
}

//使用
@FilterPath({"/update","/add"})
public class A { }

但在Java8新增了@Repeatable注解后就可以采用如下的方式定義并使用了

//使用Java8新增@Repeatable原注解
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(FilterPaths.class)//參數(shù)指明接收的注解class
public @interface FilterPath {
    String  value();
}

參考鏈接:

理解Java注解

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 什么是注解(Annotation):Annotation(注解)就是Java提供了一種元程序中的元素關(guān)聯(lián)任何信息和...
    九尾喵的薛定諤閱讀 3,415評(píng)論 0 2
  • 本文章涉及代碼已放到github上annotation-study 1.Annotation為何而來 What:A...
    zlcook閱讀 29,755評(píng)論 15 116
  • 從JDK5開始,Java增加了Annotation(注解),Annotation是代碼里的特殊標(biāo)記,這些標(biāo)記可以在...
    CarlosLynn閱讀 657評(píng)論 0 2
  • 一路浪來心也寬,群芳搖落惜春闌。 牡丹園里牡丹艷,飛絮風(fēng)中飛絮歡。 從此榮光收眼底,引誰(shuí)佳思入云端。 涼州好景應(yīng)無...
    雪窗_武立之閱讀 305評(píng)論 0 4
  • 夜色是化不開的濃稠 天上似有星 如濃稠中的泡影 酒醒時(shí)分 醉意如夜色般深邃 明日何時(shí)來 天邊染不出血紅 情人道不斷...
    無名柿子閱讀 236評(píng)論 0 0

友情鏈接更多精彩內(nèi)容