溫馨提示×

溫馨提示×

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

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java8重復注解與類型注解的區(qū)別

發(fā)布時間:2021-09-07 13:57:39 來源:億速云 閱讀:169 作者:chen 欄目:開發(fā)技術

這篇文章主要講解了“Java8重復注解與類型注解的區(qū)別”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Java8重復注解與類型注解的區(qū)別”吧!

目錄
  • Java8新特性重復注解與類型注解

    • 一、JDK5中的注解

      • 1.注解(@)

      • 2.作用

      • 3.如何理解注解?

      • 4.關于注解

      • 5.注解分為三個階段

      • 6.注解的屬性類型

      • 7.為注解增加屬性

    • 二、Java8中的注解

      • 1.類型注解

      • 2.重復注解

    • 三、Java8對注解的增強

    Java8新特性重復注解與類型注解

    在Java8之前,在某個類或者方法,字段或者參數(shù)上標注注解時,同一個注解只能標注一次。但是在Java8中,新增了重復注解和類型注解,也就是說,從Java8開始,支持在某個類或者方法,字段或者參數(shù)上標注多個相同的注解。那么,有讀者就會問了:如何實現(xiàn)呢?別急,往下看!文中不只是Java8中的注解。

    一、JDK5中的注解

    1.注解(@)

    注解就相當于一種標記,在程序中加了注解就等于為程序加了某種標記。(JDK1.5新特性)。

    2.作用

    告訴javac編譯器或者java開發(fā)工具……向其傳遞某種信息,作為一個標記。

    3.如何理解注解?

    一個注解就是一個類。

    標記可以加在包、類、字段、方法,方法參數(shù)以及局部變量上??梢酝瑫r存在多個注解。

    每一個注解結尾都沒有“;”或者其他特別符號。

    定義注解需要的基礎注解信息如下所示。

    @SuppressWarnings("deprecation")  //編譯器警告過時(source階段)
    @Deprecated      //過時(Runtime階段)
    @Override      //重寫(source階段)
    @Retention(RetentionPolicy.RUNTIME) 
    //保留注解到程序運行時。(Runtime階段)
    @Target({ElementType.METHOD,ElementType.TYPE})
    //標記既能定義在方法上,又能定義在類、接口、枚舉上等。

    注意:

    1)添加注解需要有注解類。RetentionPolicy是一個枚舉類(有三個成員)。

    2)Target中可以存放數(shù)組。它的默認值為任何元素。

    • ElementType.METHOD:表示只能標記在方法上。

    • ElementType.TYPE:表示只能標記定義在類上、接口上、枚舉上等

    3)ElementType也是枚舉類。成員包括:ANNOTATION_TYPE(注解)、CONSTRUCTOR(構造方法)、FIEID(成員變量)、LOCAL_VARIABLE(變量)、METHOD(方法)、PACKAGE(包)、PARAMETER(參數(shù))、TYPE。

    4.關于注解
    • 元注解:注解的注解(理解:給一個注解類再加注解)

    • 元數(shù)據(jù):數(shù)據(jù)的數(shù)據(jù)

    • 元信息:信息的信息

    5.注解分為三個階段

    java源文件–> class文件 --> 內存中的字節(jié)碼。

    Retention的注解有三種取值:(分別對應注解的三個階段)

    • RetentionPolicy.SOURCE

    • RetentionPolicy.CLASS

    • RetentionPolicy.RUNTIME

    注意:注解的默認階段是Class。

    6.注解的屬性類型

    原始類型(就是八個基本數(shù)據(jù)類型)、String類型、Class類型、數(shù)組類型、枚舉類型、注解類型。

    7.為注解增加屬性

    value:是一個特殊的屬性,若在設置值時只有一個value屬性需要設置或者其他屬性都采用默認值時 ,那么value=可以省略,直接寫所設置的值即可。

    例如:

    @SuppressWarnings("deprecation")

    為屬性指定缺省值(默認值):

    例如:

    String value() default "blue"; //定義在注解類中

    數(shù)組類型的屬性:

    例如:

    int[] arrayArr() default {3,4,5,5};//定義在注解類中
    SunAnnotation(arrayArr={3,9,8}) //設置數(shù)組值

    注意:如果數(shù)組屬性中只有一個元素時,屬性值部分可以省略大括號。

    例如:

    SunAnnotation(arrayArr=9)

    枚舉類型的屬性:
    例如:

    EnumDemo.TrafficLamp lamp()

    枚舉類型屬性, 定義在注解類中,這里使用了自定義的枚舉類EnumDemo.java并沒有給出相關代碼,這里只是舉個例子

    default EnumDemo.TrafficLamp.RED;

    注解類型的屬性:
    例如:

    MetaAnnotation annotationAttr()
    //定義在一個注解類中,并指定缺省值,
    //此屬性關聯(lián)到注解類:MetaAnnotation.java, 
    default @MetaAnnotation("lhm");
    //設置注解屬性值
    @SunAnnotation(annotationAttr=@MetaAnnotation("flx"))

    二、Java8中的注解

    對于注解(也被稱做元數(shù)據(jù)),Java 8 主要有兩點改進:類型注解和重復注解。

    1.類型注解

    1)Java 8 的類型注解擴展了注解使用的范圍。

    在java 8之前,注解只能是在聲明的地方所使用,java8開始,注解可以應用在任何地方。

    例如:

    創(chuàng)建類實例

    new @Interned MyObject();

    類型映射

    myString = (@NonNull String) str;

    implements 語句中

    class UnmodifiableList<T> implements@Readonly List<@Readonly T> { ... }

    throw exception聲明

    void monitorTemperature() throws@Critical TemperatureException { ... }

    注意:

    在Java 8里面,當類型轉化甚至分配新對象的時候,都可以在聲明變量或者參數(shù)的時候使用注解。
    Java注解可以支持任意類型。

    類型注解只是語法而不是語義,并不會影響java的編譯時間,加載時間,以及運行時間,也就是說,編譯成class文件的時候并不包含類型注解。

    2)新增ElementType.TYPE_USE 和ElementType.TYPE_PARAMETER(在Target上)

    新增的兩個注釋的程序元素類型 ElementType.TYPE_USE 和 ElementType.TYPE_PARAMETER用來描述注解的新場合。

    • ElementType.TYPE_PARAMETER 表示該注解能寫在類型變量的聲明語句中。

    • ElementType.TYPE_USE 表示該注解能寫在使用類型的任何語句中(例如:聲明語句、泛型和強制轉換語句中的類型)。

    例如,下面的示例。

    @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
    @interface MyAnnotation {}

    3)類型注解的作用

    類型注解被用來支持在Java的程序中做強類型檢查。配合第三方插件工具Checker Framework(注:此插件so easy,這里不介紹了),可以在編譯的時候檢測出runtime error(例如:UnsupportedOperationException; NumberFormatException;NullPointerException異常等都是runtime error),以提高代碼質量。這就是類型注解的作用。

    注意:使用Checker Framework可以找到類型注解出現(xiàn)的地方并檢查。

    例如下面的代碼。

    import checkers.nullness.quals.*;
    public class TestDemo{
        void sample() {
            @NonNull Object my = new Object();
        }
    }

    使用javac編譯上面的類:(當然若下載了Checker Framework插件就不需要這么麻煩了)

    javac -processor checkers.nullness.NullnessChecker TestDemo.java

    上面編譯是通過的,但若修改代碼:

    @NonNull Object my = null;

    但若不想使用類型注解檢測出來錯誤,則不需要processor,正常javac TestDemo.java是可以通過編譯的,但是運行時會報 NullPointerException 異常。

    為了能在編譯期間就自動檢查出這類異常,可以通過類型注解結合 Checker Framework 提前排查出來錯誤異常。

    注意java 5,6,7版本是不支持注解@NonNull,但checker framework 有個向下兼容的解決方案,就是將類型注解@NonNull 用/**/注釋起來。

    import checkers.nullness.quals.*;
    public class TestDemo{
        void sample() {
            /*@NonNull*/ Object my = null;
        }
    }

    這樣javac編譯器就會忽略掉注釋塊,但用checker framework里面的javac編譯器同樣能夠檢測出@NonNull錯誤。
    通過 類型注解 + checker framework 可以在編譯時就找到runtime error。

    2.重復注解

    允許在同一聲明類型(類,屬性,或方法)上多次使用同一個注解。

    Java8以前的版本使用注解有一個限制是相同的注解在同一位置只能使用一次,不能使用多次。

    Java 8 引入了重復注解機制,這樣相同的注解可以在同一地方使用多次。重復注解機制本身必須用 @Repeatable 注解。

    實際上,重復注解不是一個語言上的改變,只是編譯器層面的改動,技術層面仍然是一樣的。

    例如,我們可以使用如下示例來具體對比Java8之前的版本和Java8中的注解。

    **1)**自定義一個包裝類Hints注解用來放置一組具體的Hint注解

    @interface MyHints {
        Hint[] value();
    }
     
    @Repeatable(MyHints.class)
    @interface Hint {
        String value();
    }

    使用包裝類當容器來存多個注解(舊版本方法)

    @MyHints({@Hint("hint1"), @Hint("hint2")})
    class Person {}

    使用多重注解(新方法)

    @Hint("hint1")
    @Hint("hint2")
    class Person {}

    **2)**完整類測試如下所示。

    public class RepeatingAnnotations {
        @Target(ElementType.TYPE)
        @Retention(RetentionPolicy.RUNTIME)
        public @interface Filters {
            Filter[] value();
        }
        
        @Target(ElementType.TYPE)
        @Retention(RetentionPolicy.RUNTIME)
        @Repeatable(Filters.class)
        public @interface Filter {
            String value();
        }
        @Filter("filter1")
        @Filter("filter2")
        public interface Filterable {
        }
        public static void main(String[] args) {
            for (Filter filter : Filterable.class.getAnnotationsByType(Filter.class)) {
                System.out.println(filter.value());
            }
        }
    }

    輸出結果:

    filter1
    filter2

    分析:

    注釋Filter被@Repeatable( Filters.class )注釋。Filters 只是一個容器,它持有Filter, 編譯器盡力向程序員隱藏它的存在。通過這樣的方式,F(xiàn)ilterable接口可以被Filter注釋兩次。

    另外,反射的API提供一個新方法getAnnotationsByType() 來返回重復注釋的類型(注意Filterable.class.getAnnotation( Filters.class )將會返回編譯器注入的Filters實例。

    **3)**java 8之前也有重復使用注解的解決方案,但可讀性不好。

    public @interface MyAnnotation {  
         String role();  
    }  
     
    public @interface Annotations {  
        MyAnnotation[] value();  
    }  
     
    public class RepeatAnnotationUseOldVersion {  
        @Annotations({@MyAnnotation(role="Admin"),@MyAnnotation(role="Manager")})  
        public void doSomeThing(){  
        }  
    }

    Java8的實現(xiàn)方式(由另一個注解來存儲重復注解,在使用時候,用存儲注解Authorities來擴展重復注解),可讀性更強。

    @Repeatable(Annotations.class) 
    public @interface MyAnnotation {  
         String role();  
    }  
     
    public @interface Annotations {  
        MyAnnotation[] value();  
    }  
     
    public class RepeatAnnotationUseOldVersion {  
     @MyAnnotation(role="Admin")  
        @MyAnnotation(role="Manager")
        public void doSomeThing(){  
        }  
    }

    什么?沒看懂?那就再來一波?。?!

    三、Java8對注解的增強

    Java 8對注解處理提供了兩點改進:可重復的注解及可用于類型的注解??傮w來說,比較簡單,下面,我們就以實例的形式來說明Java8中的重復注解和類型注解。

    首先,我們來定義一個注解類BingheAnnotation,如下所示。

    package io.mykit.binghe.java8.annotition;
    
    import java.lang.annotation.*;
    
    /**
     * @author binghe
     * @version 1.0.0
     * @description 定義注解
     */
    @Repeatable(BingheAnnotations.class)
    @Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR, ElementType.LOCAL_VARIABLE,ElementType.TYPE_PARAMETER})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface BingheAnnotation {
        String value();
    }

    注意:在BingheAnnotation注解類上比普通的注解多了一個@Repeatable(BingheAnnotations.class)注解,有小伙伴會問:這個是啥???這個就是Java8中定義可重復注解的關鍵,至于BingheAnnotations.class,大家別急,繼續(xù)往下看就明白了。

    接下來,咱們定義一個BingheAnnotations注解類,如下所示。

    package io.mykit.binghe.java8.annotation;
    
    import java.lang.annotation.*;
    
    /**
     * @author binghe
     * @version 1.0.0
     * @description 定義注解
     */
    @Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR, ElementType.LOCAL_VARIABLE,ElementType.TYPE_PARAMETER})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface BingheAnnotations {
        BingheAnnotation[] value();
    }

    看到這里,大家明白了吧??!沒錯,BingheAnnotations也是一個注解類,它相比于BingheAnnotation注解類來說,少了一個@Repeatable(BingheAnnotations.class)注解,也就是說,BingheAnnotations注解類的定義與普通的注解幾乎沒啥區(qū)別。值得注意的是,我們在BingheAnnotations注解類中,定義了一個BingheAnnotation注解類的數(shù)組,也就是說,在BingheAnnotations注解類中,包含有多個BingheAnnotation注解。所以,在BingheAnnotation注解類上指定@Repeatable(BingheAnnotations.class)來說明可以在類、字段、方法、參數(shù)、構造方法、參數(shù)上重復使用BingheAnnotation注解。

    接下來,我們創(chuàng)建一個Binghe類,在Binghe類中定義一個init()方法,在init方法上,重復使用@BingheAnnotation注解指定相應的數(shù)據(jù),如下所示。

    package io.mykit.binghe.java8.annotation;
    
    /**
     * @author binghe
     * @version 1.0.0
     * @description 測試注解
     */
    @BingheAnnotation("binghe")
    @BingheAnnotation("class")
    public class Binghe {
    
        @BingheAnnotation("init")
        @BingheAnnotation("method")
        public void init(){
    
        }
    }

    到此,我們就可以測試重復注解了,創(chuàng)建類BingheAnnotationTest,對重復注解進行測試,如下所示。

    package io.mykit.binghe.java8.annotation;
    
    import java.lang.reflect.Method;
    import java.util.Arrays;
    
    /**
     * @author binghe
     * @version 1.0.0
     * @description 測試注解
     */
    public class BingheAnnotationTest {
    
        public static void main(String[] args) throws NoSuchMethodException {
            Class<Binghe> clazz = Binghe.class;
            BingheAnnotation[] annotations = clazz.getAnnotationsByType(BingheAnnotation.class);
            System.out.println("類上的重復注解如下:");
            Arrays.stream(annotations).forEach((a) -> System.out.print(a.value() + " "));
    
            System.out.println();
            System.out.println("=============================");
    
            Method method = clazz.getMethod("init");
            annotations = method.getAnnotationsByType(BingheAnnotation.class);
            System.out.println("方法上的重復注解如下:");
            Arrays.stream(annotations).forEach((a) -> System.out.print(a.value() + " "));
        }
    }

    運行main()方法,輸出如下的結果信息。

    類上的重復注解如下:
    binghe class 
    =============================
    方法上的重復注解如下:
    init method

    感謝各位的閱讀,以上就是“Java8重復注解與類型注解的區(qū)別”的內容了,經過本文的學習后,相信大家對Java8重復注解與類型注解的區(qū)別這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

    向AI問一下細節(jié)

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

    AI