溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何理解注解

發(fā)布時(shí)間:2021-10-23 09:45:22 來源:億速云 閱讀:137 作者:iii 欄目:編程語(yǔ)言

這篇文章主要講解了“如何理解注解”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“如何理解注解”吧!

本文主要內(nèi)容如下:

如何理解注解

背景

現(xiàn)在已經(jīng)處于注解盛行時(shí)代,注解@Override ,這個(gè)注解是再熟悉不過了,還有@Controller、@RequestMapping、@Service.....

注解已經(jīng)是作為一個(gè)開發(fā)中必備的技能了。

如果在面試中被問到注解,說不出個(gè)123,就只能回去等通知了。

什么是注解?

注解annotation是JavaSE5.0中新增功能。可以理解為注解是一種標(biāo)記,這種標(biāo)記可以在編譯、類加載、運(yùn)行時(shí)被讀取,并執(zhí)行相應(yīng)的處理。

它可以添加到程序的任何元素上:包聲明、類型聲明、構(gòu)造方法、普通方法、成員變量、參數(shù)。

注解的老大:

package java.lang.annotation;
    //是個(gè)接口
    public interface Annotation {    
        boolean equals(Object obj);
        int hashCode(); 
        String toString(); 
        //獲取注解類型
        Class<? extends Annotation> annotationType();
    }

JDK自帶為我們提供了元注解,下面就來聊聊JDK的元注解。

元注解有哪些?

JDK為我們提供五個(gè)元注解,位于java.lang.annotation 包目錄下。分別為:

  • @Retention

  • @Target

  • @Documented

  • @Inherited

  • @Repeatable

Retention注解

字面翻譯:

如何理解注解

該注解就是定義該注解是作用于什么階段。

編譯、類加載、運(yùn)行(使用最多)

注解源碼:

@Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Retention {
        /**
         * Returns the retention policy.
         * @return the retention policy
         */
        RetentionPolicy value();
    }

保留策略

package java.lang.annotation;
    public enum RetentionPolicy { 
        SOURCE, 
        CLASS, 
        RUNTIME
    }

SOURCE:只在Java源代碼中,編譯器編譯的時(shí)候會(huì)把它直接丟棄。

CLASS:編譯器將注解記錄在.class文件中。當(dāng)運(yùn)行Java程序時(shí),JVM不能獲取注解信息。這是默認(rèn)值。

RUNTIME:編譯器將注解記錄在.class文件中。當(dāng)運(yùn)行Java程序時(shí),JVM也能獲取注解信息。程序可以通過反射獲取注解信息。

注意:如果使用該注解的時(shí)候必須指定value的值。

Target注解

字面意義為目標(biāo),下面來看看器源碼:

@Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Target {
        //元素類型
        ElementType[] value();
    }
    public enum ElementType { 
        TYPE, 
        FIELD, 
        METHOD, 
        PARAMETER, 
        CONSTRUCTOR, 
        LOCAL_VARIABLE, 
        ANNOTATION_TYPE, 
        PACKAGE, 
        /** @since 1.8*/
        TYPE_PARAMETER,
        /** @since 1.8*/
        TYPE_USE
    }

Target注解是在聲明創(chuàng)建一個(gè)注解的時(shí)候,指示該注解可以作用于程序中的哪些元素。

它里邊也包含一個(gè)名為value的成員變量,value成員變量的值有如下幾種

  • ANNOTATION_TYPE:指定當(dāng)前注解只能修飾其它注解

  • CONSTRUCTOR:指定當(dāng)前注解只能修飾構(gòu)造方法

  • FIELD:指定當(dāng)前注解只能修飾成員變量

  • LOCAL_VARIABLE:指定當(dāng)前注解只能修飾局部變量

  • METHOD:指定當(dāng)前注解只能修飾方法

  • PACKAGE:指定當(dāng)前注解只能修飾包

  • PARAMETER:指定當(dāng)前注解只能修飾參數(shù)

  • TYPE:指定當(dāng)前注解可以修飾類,接口,其它注解,枚舉等類型

比如說:常用的Spring的注解@Controller,是用來作用于類上的。

如何理解注解

Documented 注解

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

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

主要是用來生成文檔的,工作中基本上很少使用,作為了解就可以了。

Inherited 注解

字面意義:

如何理解注解

@Inherited注解是在聲明創(chuàng)建一個(gè)注解的時(shí)候,指定該注解將具有繼承性。

Inherited 源碼:

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

如果某個(gè)類使用了該注解@Xx,則其子類也將自動(dòng)被此注解@Xx所修飾。

使用模板:

@Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Inherited
    public @interface A {
        
    }
    @A
    public class Base {
        
    }
    //Sub也帶有@A
    public class Sub extends Base {
    }

Repeatable注解

@Repeatable元注解,顧名思義,重復(fù)注解,就是在聲明創(chuàng)建注解的時(shí)候,指定該注解可以被同一個(gè)程序元素多次使用。這是JDK8新增的注解,重復(fù)注解只是一種簡(jiǎn)單化寫法,這種簡(jiǎn)單化寫法是一種假象。多個(gè)重復(fù)注解其實(shí)會(huì)被作為“容器”注解的value成員變量的數(shù)組元素。

比如下面Spring中注解ComponentScan:

@Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Repeatable(ComponentScans.class)
    public @interface ComponentScan {
    }
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    public @interface ComponentScans {
     ComponentScan[] value();
    }

使用案例

import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.ComponentScans;
    
    @ComponentScans(value = {
            @ComponentScan(value = "com.tian.pakage0"),
            @ComponentScan(value = "com.tian.pakage1")})
    public class MainConfig {
        @Bean
        public User person() {
            return new User();
        }
    }

基本注解

注解必須使用工具來處理,工具負(fù)責(zé)提取注解中包含的元數(shù)據(jù),工具還會(huì)根據(jù)這些元數(shù)據(jù)增加額外的功能。下面我們先來了解一下5個(gè)基本注解的用法。

  • Override

  • SafeVarargs

  • SuppressWarnings

  • FunctionalInterface

  • Deprecated

下面我們就來說說這五個(gè)注解。

Override

用于標(biāo)識(shí)方法,標(biāo)識(shí)該方法屬于重寫父類的方法 。

//只能用于方法上
    @Target(ElementType.METHOD)
    //源碼中編譯時(shí)就會(huì)使用
    @Retention(RetentionPolicy.SOURCE)
    public @interface Override {
    }

此注解表面是重寫父類的方法,只能用于方法上,并且用于編譯階段,我們?cè)陂_發(fā)的時(shí)候,如果注解使用不當(dāng),在源碼編譯時(shí)立馬就會(huì)做出提示。

SafeVarargs

@SafeVarargs注解是在JDK7中引入的。此注解適用于接受varargs參數(shù)的final和static方法或構(gòu)造函數(shù)。此注解用于確保方法不會(huì)對(duì)其varargs參數(shù)執(zhí)行不安全的操作。從Java9開始,@SafeVarargs注解也適用于私有實(shí)例方法。

@SafeVarargs源碼

@Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
    public @interface SafeVarargs {}

使用案例

如果不使用此注解

public class SafevarargsTest {
    public static void main(String[] args) {
        // 傳遞可變參數(shù),參數(shù)是泛型集合
        display(10, 20, 30);
        // 傳遞可變參數(shù),參數(shù)是非泛型集合
        display("10", 20, 1000L); // 會(huì)有編譯警告
    }
    public static <T> void display(T... array) {
        for (T arg : array) {
            System.out.println(arg.getClass().getName() + ":" + arg);
        }
    }
}

display方法會(huì)提示

如何理解注解

如果我們給這個(gè)方法添加此注解后

如何理解注解

上面的代碼在可變參數(shù)display前添加了@SafeVarargs注解,當(dāng)然也可以使用 @SuppressWarnings("unchecked") 注解,可是,兩者相比較來說的話@SafeVarargs注解更適合。

注意:@SafeVarargs注解不適用于非static或非final聲明的方法,對(duì)于未聲明為static或final的方法,假如,要抑制unchecked警告,可以使用@SuppressWarnings注解。

SuppressWarnings

用于有選擇的關(guān)閉編譯器對(duì)類、方法、成員變量、變量初始化的警告。

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

編譯時(shí)期就會(huì)做出提示,使用范圍也是比較廣泛,可以適用于類、接口、枚舉、方法、字段等。

我們開發(fā)過程中見過最多的應(yīng)該是集合存放數(shù)據(jù)的時(shí)候,比如下面的例子。

如何理解注解

但是如果我們把第一行代碼注釋放開。

如何理解注解

這樣就不會(huì)提示了。

FunctionalInterface

JDK8新增注解。@FunctionalInterface標(biāo)記在接口上,“函數(shù)式接口”是指僅僅只包含一個(gè)抽象方法的接口。

源碼

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

1、該注解只能標(biāo)記在”有且僅有一個(gè)抽象方法”的接口上。

2、JDK8接口中的靜態(tài)方法和默認(rèn)方法,都不算是抽象方法。

3、接口默認(rèn)繼承java.lang.Object,所以如果接口顯示聲明覆蓋了Object中方法,那么也不算抽象方法。

4、該注解不是必須的,如果一個(gè)接口符合”函數(shù)式接口”定義,那么加不加該注解都沒有影響。加上該注解能夠更好地讓編譯器進(jìn)行檢查。如果編寫的不是函數(shù)式接口,但是加上了@FunctionInterface,那么編譯器會(huì)報(bào)錯(cuò)。

5、@FunctionalInterface 注解的interface。它的特點(diǎn)是其中只有一個(gè)子類必須要實(shí)現(xiàn)的abstract方法。

使用場(chǎng)景

Callable、Runnable等就有使用到。

@FunctionalInterface
    public interface Callable<V> {
        V call() throws Exception;
    }
    @FunctionalInterface
    public interface Runnable {
        public abstract void run();
    }

Deprecated

用于標(biāo)識(shí)方法或類,標(biāo)識(shí)該類或方法已過時(shí),建議不要使用 。

@Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
    public @interface Deprecated {
    }

使用范圍就想到廣泛了,構(gòu)造方法、字段、方法等。

注意:只是提示過時(shí)了,不建議使用,不代表不能用,但是我們?nèi)绻胗媚硞€(gè)使用此注解標(biāo)記的方法或者類的時(shí)候,建議找找有沒有替換方案,實(shí)在沒有替換方案,搞清楚為什么它會(huì)被設(shè)置成過時(shí),使用不當(dāng)可能會(huì)對(duì)我們的程序造成你意想不到問題,也可能會(huì)挖坑。

自定義注解

終于來到自定義了,下面我們來自定義一個(gè)注解。

使用@interface自定義注解時(shí),自動(dòng)繼承了java.lang.annotation.Annotation接口,由編譯程序自動(dòng)完成其他細(xì)節(jié)。在定義注解時(shí),不能繼承其他的注解或接口。@interface用來聲明一個(gè)注解,其中的每一個(gè)方法實(shí)際上是聲明了一個(gè)配置參數(shù)。方法的名稱就是參數(shù)的名稱,返回值類型就是參數(shù)的類型(返回值類型只能是基本類型、Class、String、enum)??梢酝ㄟ^default來聲明參數(shù)的默認(rèn)值。

格式

public @interface 注解名 {定義體}

案例

自定義我們的注解

@Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface  MyInterface {
        String value() default "";
    }

使用階段為運(yùn)行階段,目標(biāo)是在方法上。定義一個(gè)屬性value默認(rèn)值為空字符串。

下面我們就來使用我們定義的注解。

import java.lang.reflect.Method;
    
    public class InterfaceDemo {
    
        //@MyInterface("老田自定義的注解")
         //@MyInterface
        @MyInterface(value = "老田自定義的注解")
        public void test() {
            // TODO:
        }
    
        public static void main(String[] args) {
            InterfaceDemo interfaceDemo = new InterfaceDemo();
            Class<?> clazz = interfaceDemo.getClass();
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                if ("test".contentEquals(method.getName())) {
                    System.out.println("方法名稱= ">

如果沒有指定value的值,那就使用默認(rèn)值,value兩種方式賦值:

@MyInterface("老田自定義的注解")
    @MyInterface(value = "老田自定義的注解")

然后我們運(yùn)行上面的代碼,輸出結(jié)果為:

如何理解注解

以上我們就搞定了自定義注解以及使用,獲取屬性值。

注解是普通類還是接口?

使用javap查看我們自定義的注解.class文件內(nèi)容。

如何理解注解

這里Annotation是接口:

public interface Annotation {
    }

那么證明,我們自定義的注解是extend接口Annotation,由此可知注解就是接口。

自定義注解注意點(diǎn)

解參數(shù)的可支持?jǐn)?shù)據(jù)類型:

  • 所有基本數(shù)據(jù)類型(int,float,boolean,byte,double,char,long,short)

  • String類型

  • Class類型

  • enum類型

  • Annotation類型

  • 以上所有類型的數(shù)組

Annotation類型里面的參數(shù)該怎么設(shè)定:

  • 只能用public或默認(rèn)(default)這兩個(gè)訪問權(quán)修飾.例如,String value();這里把方法設(shè)為defaul默認(rèn)類型。

  • 參數(shù)成員只能用基本類型byte,short,char,int,long,float,double,boolean八種基本數(shù)據(jù)類型 和 String,Enum,Class,annotations等數(shù)據(jù)類型,以及這一些類型的數(shù)組.例如,String value();這里的參數(shù)成員就為String。

  • 如果只有一個(gè)參數(shù)成員,最好把參數(shù)名稱設(shè)為"value",后加小括號(hào).例:下面的例子FruitName注解就只有一個(gè)參數(shù)成員。

如何獲取注解?

共有以下五種方式:

  1. 獲取類上的注解:Class類的getAnnotation()

  2. 獲取方法上的注解:Method類的getAnnotation()

  3. 獲取字段上的注解:Field類的 getAnnotation()

  4. 獲取構(gòu)造方法上的注解:Constructor類的getAnnotation()

  5. 獲取包上的注解:Package類的getAnnotation()

如果此元素上存在指定的注釋類型,則此方法返回該元素的注釋,否則返回null。從上面的的集中方式中發(fā)現(xiàn),都是使用getAnnotation()方法獲取的,相信大多數(shù)人都能猜到為什么都是同一個(gè)方法名稱。

下面就來說說Java中注解獲取的鼻祖:

java.lang.reflect.AnnotatedElement

此接口所有方法

如何理解注解

看看AnnotatedElement類圖:

如何理解注解

發(fā)現(xiàn)前面說的獲取注解的類,全部都實(shí)現(xiàn)了AnnotatedElement接口。

所以程序通過反射獲取了某個(gè)類的AnnotatedElement對(duì)象之后,程序就可以調(diào)用該對(duì)象的方法。

如下四個(gè)個(gè)方法來訪問Annotation信息:

「getAnnotation」

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

「getAnnotations」

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

「isAnnotationPresent」

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

「getDeclaredAnnotations」

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

感謝各位的閱讀,以上就是“如何理解注解”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)如何理解注解這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI