java - 注解

1. 元注解

元注解:用在注解上的注解,java1.5后添加的4個(gè)元注解:

  • @Target
  • @Retention
  • @Documented
  • @Inherited

在java1.8又添加了兩個(gè)注解:

  • @Native
  • @Repeatable

先說明下這幾個(gè)注解.

@Target 修飾的對(duì)象范圍

取值(ElementType)有:

  • 1.CONSTRUCTOR:用于描述構(gòu)造器
  • 2.FIELD:用于描述域
  • 3.LOCAL_VARIABLE:用于描述局部變量
  • 4.METHOD:用于描述方法
  • 5.PACKAGE:用于描述包
  • 6.PARAMETER:用于描述參數(shù)
  • 7.TYPE:用于描述類、接口(包括注解類型) 或enum聲明

當(dāng)注解類型聲明中沒有@Target元注解,則默認(rèn)為可適用所有的程序元素。

@Retention 注解保留到哪個(gè)時(shí)期(生命周期)

取值(RetentionPoicy)有:

  • 1.SOURCE:在源文件中有效(即源文件保留)
  • 2.CLASS:在class文件中有效(即class保留)
  • 3.RUNTIME:在運(yùn)行時(shí)有效(即運(yùn)行時(shí)保留)

默認(rèn)CLASS,可以看下源碼中提解釋:

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
}

@Documented 標(biāo)志可文檔化

被標(biāo)注的程序成員的公共API,因此可以被例如javadoc此類的工具被標(biāo)注的程序成員的公共API,因此可以被例如javadoc此類的工具文檔化被標(biāo)注的程序成員的公共API,因此可以被例如javadoc此類的工具文檔化

@Inherited 將注解內(nèi)容傳遞到子類

若是方法被重寫則得不到父類方法中的注解內(nèi)容,關(guān)于該方法所有的內(nèi)容都被重新定義了.

在類上的注解會(huì)被傳遞該子類,方法也可以但重寫了則不會(huì)在傳遞給子類
再次捋清楚下:

  • 如果父類的注解是定義在類上面,那么子類是可以繼承過來的
  • 如果父類的注解定義在方法上面,那么子類仍然可以繼承過來
  • 如果子類重寫了父類中定義了注解的方法,那么子類將無法繼承該方法的注解
  • 即子類在重寫父類中被@Inherited標(biāo)注的方法時(shí),會(huì)將該方法連帶它上面的注解一并覆蓋掉

2.常見的Java注解

我們平時(shí)直接使用java提供幾個(gè)注解:@Override @Deprecated @SuppressWarnings @FunctionalInterface,下面逐個(gè)看下其定義

  • @Override :告知服務(wù)器,我們要覆蓋父類中的當(dāng)前方法.

     @Target(ElementType.METHOD)
     @Retention(RetentionPolicy.SOURCE)
     public @interface Override {
     }
    
  • @Deprecated :告知編譯器,某一程序元素不建議使用了

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
    public @interface Deprecated {
    }
    
  • @SuppressWarnings 忽略特定的警告
    用于告知編譯器忽略特定的警告信息,例在泛型中使用原生數(shù)據(jù)類型,編譯器會(huì)發(fā)出警告,當(dāng)使用該注解后,則不會(huì)發(fā)出警告。

    @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
    @Retention(RetentionPolicy.SOURCE)
    public @interface SuppressWarnings {
        String[] value();
    }
    

    可以接受多個(gè)參數(shù)例如:
    @SupressWarning(value={"uncheck","deprecation"})

    接受的類型:

    • all: to suppress all warnings
    • boxing: to suppress warnings relative to boxing/unboxing operations
    • cast: to suppress warnings relative to cast operations
    • dep-ann: to suppress warnings relative to deprecated annotation
    • deprecation: to suppress warnings relative to deprecation
    • fallthrough: to suppress warnings relative to missing breaks in switch statements
    • finally: to suppress warnings relative to finally block that don’t return
    • hiding: to suppress warnings relative to locals that hide variable
    • incomplete-switch: to suppress warnings relative to missing entries in a switch statement (enum case)
    • nls: to suppress warnings relative to non-nls string literals
    • null: to suppress warnings relative to null analysis
    • rawtypes: to suppress warnings relative to un-specific types when using generics on class params
    • restriction: to suppress warnings relative to usage of discouraged or forbidden references
    • serial: to suppress warnings relative to missing serialVersionUID field for a serializable class
    • static-access: to suppress warnings relative to incorrect static access
    • synthetic-access: to suppress warnings relative to unoptimized access from inner classes
    • unchecked: to suppress warnings relative to unchecked operations
    • unqualified-field-access: to suppress warnings relative to field access unqualified
    • unused: to suppress warnings relative to unused code
  • @FunctionalInterface 保證該接口是函數(shù)式接口
    用戶告知編譯器,檢查這個(gè)接口,保證該接口是函數(shù)式接口,即只能包含一個(gè)抽象方法,否則就會(huì)編譯出錯(cuò)。

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

3. 自定義注解

Annotaion不影響程序代碼的執(zhí)行,無論增加、刪除Annotation,代碼都始終如一地執(zhí)行。
也就是說:如果我們不去解析注解,它就沒什么作用和影響.
在 java.lang.Class 中末尾有4個(gè)涉及的解析注解的方法:

  • <T extends Annotation> T getAnnotation(Class<T> annotationClass)

    返回改程序元素上存在的、指定類型的注解,如果該類型注解不存在,則返回null。

  • Annotation[] getAnnotations()

    返回該程序元素上存在的所有注解。

  • boolean isAnnotationPresent(Class<?extends Annotation> annotationClass)

    判斷該程序元素上是否包含指定類型的注解,存在則返回true,否則返回false.

  • Annotation[] getDeclaredAnnotations()

    返回直接存在于此元素上的所有注釋。與此接口中的其他方法不同,該方法將忽略繼承的注釋。(如果沒有注釋直接存在于此元素上,則返回長度為零的一個(gè)數(shù)組。)該方法的調(diào)用者可以隨意修改返回的數(shù)組;這不會(huì)對(duì)其他調(diào)用者返回的數(shù)組產(chǎn)生任何影響。

除此之外還有寫注意事項(xiàng):

  • 成員類型受限:原始類型,String,Class,Annotation,Enumeration
  • 注解類只有一個(gè)成員時(shí),必須取名為value(),在使用時(shí)可以忽略成員名和賦值號(hào)(=)
  • 注解類可以沒有成員,稱之為標(biāo)識(shí)注解

4. 實(shí)踐自定義注解

首先看下接下來要實(shí)現(xiàn)的相關(guān)類:


類結(jié)構(gòu)

FruitName 水果名稱

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FruitName {

    String value() default "";

}

FruitColor 水果顏色

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FruitColor {

    public enum Color {
        BLUE("藍(lán)色"), RED("紅色"), GREEN("綠色");

        private String name;

        public String getName() {
            return name;
        }

        Color(String name) {
            this.name = name;
        }
    }

    Color value() default Color.GREEN;
}

FruitProvider 供應(yīng)商信息

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface FruitProvider {

    int id() default 1;

    String address() default "";

    String providerName() default "";

}

接下來到使用類了

Apple 使用注解

public class Apple {

    @FruitName("蘋果")
    public String name;

    @FruitColor(FruitColor.Color.GREEN)
    public String color;

    @FruitProvider(id=1001,address = "美國硅谷",providerName = "蘋果公司")
    public String  provider;

}

FruitAnnotationParser 注解解析類

public class FruitAnnotationParser {

    public static void parse() {
        Field[] fields = Apple.class.getDeclaredFields();

        for (Field field : fields) {
            if (field.isAnnotationPresent(FruitName.class)) {
                FruitName fruitName = field.getAnnotation(FruitName.class);
                String value = fruitName.value();
                System.out.println(value);

            } else if (field.isAnnotationPresent(FruitColor.class)) {
                FruitColor fruitColor = field.getAnnotation(FruitColor.class);
                FruitColor.Color color = fruitColor.value();
                System.out.println(color.getName());

            } else if (field.isAnnotationPresent(FruitProvider.class)) {
                FruitProvider provider = field.getAnnotation(FruitProvider.class);
                String address = provider.address();
                int id = provider.id();
                String providerName = provider.providerName();
                System.out.println(String.format("providerName:%s id:%d address:%s", providerName, id, address));

            }
            
        }
    }
}

測(cè)試

public class FruitTest {

    public static void main(String[] args) {
        FruitAnnotationParser.parse();
    }

}

輸出:

蘋果
綠色
providerName:蘋果公司 id:1001 address:美國硅谷

注解理解之后使用起來還是比較簡單的,解析注解的套路就那么4個(gè),解析的過程都差不多.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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