Java Review三十五、注解)

      網友投稿 721 2022-05-30

      文章目錄

      基本注解

      JDK 的元注解

      @Retention

      @Target

      @Documented

      @lnherited

      自定義注解

      提取注解信息

      使用注解實例

      Demo1

      Demo2

      基本注解

      Java 提供了5 個基本注解:

      @Override:讓編譯器檢查該方法是否正確地實現了覆寫。

      @Deprecated:用于表示某個程序元素( 類、 方法等) 己過時, 當其他程序使用己過時的類、 方法時,編譯器將會給出警告。

      @SuppressWamings:告訴編譯器忽略此處代碼產生的警告。

      @SafeVarargs:在聲明具有模糊類型(比如:泛型)的可變參數的構造函數或方法時,Java編譯器會報unchecked警告。鑒于這些情況,如果程序員斷定聲明的構造函數和方法的主體不會對其varargs參數執行潛在的不安全的操作,可使用@SafeVarargs進行標記,這樣的話,Java編譯器就不會報unchecked警告。

      @FunctionalInterface:Java 8 規定: 如果接口中只有一個抽象方法( 可以包含多個默認方法或多個 static方法), 該接口就是函數式接口。 @FunctionalInterface 就是用來指定某個接口必須是函數式接口。

      JDK 的元注解

      JDK 除在 java.lang下提供 5 個基本的注解之外, 還在 java.lang.annotation 包下提供了 6 個 Meta 注解 ( 元注解), 其中有 5 個元注解都用于修飾其他的注解定義。

      @Retention

      @Retention 只能用于修飾注解定義, 用于指定被修飾的注解可以保留多長時間, @Retention 包含一個 RetentionPolicy 類型的 value 成員變量, 所以使用@Retention 時必須為該 value 成員變量指定值。

      value 成員變量的值只能是如下三個:

      RetentionPolicy.CLASS: 編譯器將把注解記錄在 class 文件中。 當運行 Java 程序時, JVM 不可獲取注解信息。 這是默認值。

      RetentionPolicy.RUNTIME: 編譯器將把注解記錄在 class 文件中。 當運行 Java 程序時, JVM 也可獲取注解信息, 程序可以通過反射獲取該注解信息。

      RetentionPolicy.SOURCE: 注解只保留在源代碼中, 編譯器直接丟棄這種注解。

      如 果 需 要 通 過 反 射 獲 取 注 解 信 息 , 就 需 要 使 用 value 屬 性 值 為 RetentionPolicy.RUNTIME 的@Retention。

      使用@Retention 元注解可釆用如下代碼為 value 指定值:

      // 定義下面的(testable 注解保留到運行時 @Retention(value= RetentionPolicy.RUNTIME) public @interface Testable{}

      1

      2

      3

      也可采用如下代碼來為 value 指定值:

      // 定義下面的(testable 注解將被編譯器直接丟棄 @Retention(RetentionPolicy.SOURCE) public @interface Testable{}

      1

      2

      3

      java.lang.annotation.Retention

      @Target

      @Target 也只能修飾注解定義, 它用于指定被修飾的注解能用于修飾哪些程序單元。 @Target 元注解也包含一個名為 value 的成員變量, 該成員變量的值只能是如下幾個:

      ElementType.ANNOTATION_TYPE 指定該策略的注解只能修飾注解。

      ElementType.CONSTRUCTOR 指定該策略的注解只能修飾構造器。

      ElementType.FIELD 指定該策略的注解只能修飾成員變量。

      ElementType.LOCAL_VARIABLE 指定該策略的注解只能修飾局部變量。

      ElementType.METHOD 指定該策略的注解只能修飾方法定義。

      ElementType.PACKAGE 指定該策略的注解只能修飾包定義。

      Java Review(三十五、注解)

      ElementType.PARAMETER 指定該策略的注解可以修飾參數。

      ElementType.TYPE 指定該策略的注解可以修飾類、 接口(包括注解類型) 或枚舉定義。

      如下代碼指定@ActionListenerFor 注解只能修飾成員變量:

      ?Target(ElementType.FIELD) public @interface ActionListenerFor{}

      1

      2

      如下代碼片段指定@Testable 注解只能修飾方法:

      @Target(ElementType.METHOD) public @interface Testable { }

      1

      2

      java.lang.annotation.Target

      @Documented

      ?Documented 用于指定被該元注解修飾的注解類將被 javadoc 工具提取成文檔, 如果定義注解類時使用了?Documented 修飾, 則所有使用該注解修飾的程序元素的 API 文檔中將會包含該注解說明。

      java.lang.annotation.Documented

      @lnherited

      ?Inherited 元注解指定被它修飾的注解將具有繼承性—如果某個類使用7@Xxx 注解( 定義該注解時使用了@Inherited 修飾) 修飾, 則其子類將自動被@Xxx 修飾。

      java.lang.annotation.Inherited

      自定義注解

      Java語言使用@interface語法來定義注解(Annotation),格式如下:

      / / 定義一個簡單的注解類型 public @interface Test{ }

      1

      2

      3

      4

      在默認情況下, 注解可用于修飾任何程序元素, 包括類、 接口、 方法等, 如下程序使用@Test 來修飾方法:

      public class MyClass{ // 使用@Test 注解修飾方法 @Test public void info(){ } }

      1

      2

      3

      4

      5

      6

      7

      注解不僅可以是這種簡單的注解, 還可以帶成員變量, 成員變量在注解定義中以無形參的方法形式來聲明, 其方法名和返回值定義了該成員變量的名字和類型。

      如下代碼可以定義一個有成員變量的注解:

      public @interface MyTag{ // 定義帶兩個成員變量的注解 // 注解中的成員變量以方法的形式來定義 String name(); int age(); }

      1

      2

      3

      4

      5

      6

      注解的參數類似無參數方法,可以用default設定一個默認值(強烈推薦)。最常用的參數應當命名為value:

      public @interface MyTag{ // 定義帶兩個成員變量的注解 // 注解中的成員變量以方法的形式來定義 String name() default "牛鋼鐵"; int age() 666; }

      1

      2

      3

      4

      5

      6

      也可以在使用 MyTag 注解時為成員變量指定值, 如果為 MyTag 的成員變量指定了值, 則默認值不會起作用:

      public class MyTest{ @MyTag(name="麻球", age=6) public void info(){ } }

      1

      2

      3

      4

      5

      6

      通常會用元注解去修飾自定義注解,如上文所示。

      例如,使用@Target可以定義Annotation能夠被應用于源碼的哪些位置:

      //定義MyTag注解應用于方法上 @Target(ElementType.METHOD) public @interface MyTag{ // 定義帶兩個成員變量的注解 // 注解中的成員變量以方法的形式來定義 String name() default "牛鋼鐵"; int age() 666; }

      1

      2

      3

      4

      5

      6

      7

      8

      提取注解信息

      使用注解修飾了類、 方法、 成員變量等成員之后, 這些注解不會自己生效, 必須由開發者提供相應的工具來提取并處理注解信息。

      因為注解定義后也是一種class,所有的注解都繼承自 java.lang.annotation.Annotation,因此,讀取注解,需要使用反射API。

      Java提供的使用反射API讀取Annotation的方法包括:

      判斷某個注解是否存在于Class、Field、Method或Constructor:

      Class.isAnnotationPresent(Class)

      Field.isAnnotationPresent(Class)

      Method.isAnnotationPresent(Class)

      Constructor.isAnnotationPresent(Class)

      例如:

      // 判斷@Report是否存在于Person類: Person.class.isAnnotationPresent(Report.class);

      1

      2

      使用反射API讀取Annotation:

      Class.getAnnotation(Class)

      Field.getAnnotation(Class)

      Method.getAnnotation(Class)

      Constructor.getAnnotation(Class)

      例如:

      // 獲取Person定義的@Report注解: Report report = Person.class.getAnnotation(Report.class); int type = report.type(); String level = report.level();

      1

      2

      3

      4

      使用反射API讀取Annotation有兩種方法。方法一是先判斷Annotation是否存在,如果存在,就直接讀?。?/p>

      Class cls = Person.class; if (cls.isAnnotationPresent(Report.class)) { Report report = cls.getAnnotation(Report.class); ... }

      1

      2

      3

      4

      5

      第二種方法是直接讀取Annotation,如果Annotation不存在,將返回null:

      Class cls = Person.class; Report report = cls.getAnnotation(Report.class); if (report != null) { ... }

      1

      2

      3

      4

      5

      讀取方法、字段和構造方法的Annotation和Class類似。但要讀取方法參數的Annotation就比較麻煩一點,因為方法參數本身可以看成一個數組,而每個參數又可以定義多個注解,所以,一次獲取方法參數的所有注解就必須用一個二維數組來表示。例如,對于以下方法定義的注解:

      public void hello(@NotNull @Range(max=5) String name, @NotNull String prefix) { }

      1

      2

      要讀取方法參數的注解,我們先用反射獲取Method實例,然后讀取方法參數的所有注解:

      // 獲取Method實例: Method m = ... // 獲取所有參數的Annotation: Annotation[][] annos = m.getParameterAnnotations(); // 第一個參數(索引為0)的所有Annotation: Annotation[] annosOfName = annos[0]; for (Annotation anno : annosOfName) { if (anno instanceof Range) { // @Range注解 Range r = (Range) anno; } if (anno instanceof NotNull) { // @NotNull注解 NotNull n = (NotNull) anno; } }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      使用注解實例

      Demo1

      注解@Testable 沒有任何成員變量,僅是一個標記注解,它的作用是標記哪些方法是可測試的:

      import java.lang.annotation.*; // 使@Retention指定注解的保留到運行時 @Retention(RetentionPolicy.RUNTIME) // 使用@Target指定被修飾的注解可用于修飾方法 @Target(ElementType.METHOD) // 定義一個標記注解,不包含任何成員變量,即不可傳入元數據 public @interface Testable { }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      如下 MyTest 測試用例中定義了 8 個方法, 這 8 個方法沒有太大的區別, 其中 4 個方法使用@Testable注解來標記這些方法是可測試的:

      public class MyTest { // 使用@Testable注解指定該方法是可測試的 @Testable public static void m1() { } public static void m2() { } // 使用@Testable注解指定該方法是可測試的 @Testable public static void m3() { throw new IllegalArgumentException("參數出錯了!"); } public static void m4() { } // 使用@Testable注解指定該方法是可測試的 @Testable public static void m5() { } public static void m6() { } // 使用@Testable注解指定該方法是可測試的 @Testable public static void m7() { throw new RuntimeException("程序業務出現異常!"); } public static void m8() { } }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

      37

      Demo2

      @Range注解,我們希望用它來定義一個String字段的規則——字段長度滿足@Range的參數定義:

      @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Range { int min() default 0; int max() default 255; }

      1

      2

      3

      4

      5

      6

      在某個JavaBean中,使用注解:

      public class Person { @Range(min=1, max=20) public String name; @Range(max=10) public String city; }

      1

      2

      3

      4

      5

      6

      7

      定義了注解,本身對程序邏輯沒有任何影響。必須編寫代碼來使用注解。這里,編寫一個Person實例的檢查方法,它可以檢查Person實例的String字段長度是否滿足@Range的定義:

      void check(Person person) throws IllegalArgumentException, ReflectiveOperationException { // 遍歷所有Field: for (Field field : person.getClass().getFields()) { // 獲取Field定義的@Range: Range range = field.getAnnotation(Range.class); // 如果@Range存在: if (range != null) { // 獲取Field的值: Object value = field.get(person); // 如果值是String: if (value instanceof String) { String s = (String) value; // 判斷值是否滿足@Range的min/max: if (s.length() < range.min() || s.length() > range.max()) { throw new IllegalArgumentException("Invalid field: " + field.getName()); } } } } }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      參考:

      【1】:《瘋狂Java講義》

      【2】:廖雪峰的官方網站:使用注解

      【3】:春晨:@SafeVarargs注解的使用

      【4】:廖雪峰的官方網站:自定義注解

      【5】:廖雪峰的官方網站:處理注解

      Java

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:知乎服務化的實踐與思考
      下一篇:mysqldump備份時的數據一致性問題--single-transaction
      相關文章
      亚洲精品免费视频| 亚洲AV无码精品色午夜果冻不卡 | 自拍偷自拍亚洲精品被多人伦好爽| 亚洲av午夜国产精品无码中文字| 国产亚洲精品成人AA片| 亚洲一级毛片免费观看| 亚洲av永久无码嘿嘿嘿| 亚洲人成伊人成综合网久久| 亚洲xxxxxx| 亚洲永久在线观看| 亚洲日韩精品国产一区二区三区| 最新亚洲春色Av无码专区| 亚洲欧洲无码一区二区三区| 亚洲精品自偷自拍无码| 亚洲妇女无套内射精| 久久久久亚洲精品无码网址色欲| 亚洲1区2区3区精华液| 亚洲 自拍 另类小说综合图区 | 亚洲综合一区二区精品导航| 久久久无码精品亚洲日韩按摩| 91天堂素人精品系列全集亚洲| 亚洲第一精品电影网| 亚洲区视频在线观看| 亚洲中文字幕无码久久| 亚洲AV无码成人精品区狼人影院| 日韩国产精品亚洲а∨天堂免| 亚洲成人一区二区| 亚洲色欲色欲www在线丝| 亚洲国产精品一区二区久久hs | 亚洲AV无码一区二区大桥未久| 亚洲AV网站在线观看| 国产性爱在线观看亚洲黄色一级片| 中文亚洲AV片在线观看不卡| 亚洲αv久久久噜噜噜噜噜| 亚洲黄色免费观看| 狠狠色伊人亚洲综合网站色| 国产精品久久久久久亚洲小说| 老司机亚洲精品影视www| 亚洲日本一区二区三区| 亚洲一级毛片免费观看| 久久亚洲中文无码咪咪爱|