Java基礎(十)——列舉與註解

L發表於2022-01-17

一、列舉

1、介紹

  列舉類:類的物件只有有限個,確定的。當需要定義一組常量時,強烈建議使用列舉類。如果列舉類中只有一個物件,則可以作為單例模式的實現。
  使用 enum 定義的列舉類預設繼承了 java.lang.Enum類,因此不能再繼承其他類。

2、列舉的實現

  程式碼示例:方式一,JDK 5.0 之前,自定義列舉類

 1 public class SeasonEnum {
 2 
 3     // 列舉當前類的多個物件
 4     public static final SeasonEnum SPRING = new SeasonEnum("01", "春天");
 5     public static final SeasonEnum SUMMER = new SeasonEnum("02", "夏天");
 6     public static final SeasonEnum AUTUMN = new SeasonEnum("03", "秋天");
 7     public static final SeasonEnum WINTER = new SeasonEnum("04", "冬天");
 8 
 9     private SeasonEnum(String code, String desc) {
10         this.code = code;
11         this.desc = desc;
12     }
13 
14     private final String code;
15     private final String desc;
16 
17     public String getCode() {
18         return code;
19     }
20 
21     public String getDesc() {
22         return desc;
23     }
24 }

  程式碼示例:方式二,JDK 5.0,可以使用 enum 關鍵字定義列舉

 1 public enum SeasonEnum {
 2 
 3     // 列舉當前類的多個物件
 4     SPRING("01", "春天"),
 5     SUMMER("02", "夏天"),
 6     AUTUMN("03", "秋天"),
 7     WINTER("04", "冬天");
 8 
 9     public static final Map<String, SeasonEnum> map = new HashMap<>();
10 
11     static {
12         for (SeasonEnum e : values()) {
13             map.put(e.code, e);
14         }
15     }
16 
17     SeasonEnum(String code, String desc) {
18         this.code = code;
19         this.desc = desc;
20     }
21 
22     private final String code;
23     private final String desc;
24 
25     public String getCode() {
26         return code;
27     }
28 
29     public String getDesc() {
30         return desc;
31     }
32 
33 }

3、列舉實現介面

 1 public enum SeasonEnum implements Info {
 2 
 3     // 列舉當前類的多個物件
 4     SPRING("01", "春天") {
 5         @Override
 6         public void show() {
 7             System.out.println("春暖花開");
 8         }
 9     },
10     SUMMER("02", "夏天") {
11         @Override
12         public void show() {
13             System.out.println("夏日炎炎");
14         }
15     },
16     AUTUMN("03", "秋天"),
17     WINTER("04", "冬天");
18 
19     SeasonEnum(String code, String desc) {
20         this.code = code;
21         this.desc = desc;
22     }
23 
24     private final String code;
25     private final String desc;
26 
27     public String getCode() {
28         return code;
29     }
30 
31     public String getDesc() {
32         return desc;
33     }
34 
35     public void show() {
36         System.out.println("我的天氣");
37     }
38 
39 }
40 
41 interface Info {
42     void show();
43 }

4、API

  values()方法:返回所有的列舉型別的物件。
  valueOf(String str):檢查該字串是不是列舉類物件的"名字"。如不是,會有執行時異常:IllegalArgumentException。
  toString():返回當前列舉類物件常量的名稱。

二、註解

1、介紹

  從 JDK 5.0 開始,Java 增加了對後設資料(MetaData)的支援,也就是Annotation(註解)。
  Annotation 其實就是程式碼裡的特殊標記,這些標記可以在編譯,類載入,執行時被讀取,並執行相應的處理。通過使用 Annotation,程式設計師可以在不改變原有邏輯的情況下,在原始檔中嵌入一些補充資訊。程式碼分析工具、開發工具和部署工具可以通過這些補充資訊進行驗證或者進行部署。
  一定程度上,可以說:框架 = 註解 + 反射 + 設計模式。

2、示例

  示例一、生成文件相關

  @author:標明開發該類模組的作者,多個作者之間使用,分割。
  @version:標明該類模組的版本。
  @see:參考轉向,也就是相關主題。
  @since:從哪個版本開始增加的。
  @param:對方法中某引數的說明,如果沒有引數就不能寫。
  @return:對方法返回值的說明,如果方法的返回值型別是void就不能寫。
  @exception:對方法可能丟擲的異常進行說明,如果方法沒有用throws顯式丟擲的異常就不能寫。

  示例二、在編譯時進行格式檢查(JDK內建的三個基本註解)

  @Override:限定重寫父類方法,該註解只能用於方法。
  @Deprecated:用於表示所修飾的元素(類,方法等)已過時。通常是因為所修飾的結構危險或存在更好的選擇。
  @SuppressWarnings:抑制編譯器警告。

3、自定義註解

  程式碼示例:

 1 @Retention(RetentionPolicy.RUNTIME)
 2 @Target({ElementType.TYPE, ElementType.FIELD})
 3 public @interface MyAnnotation {
 4     String value() default "baidu";
 5 }
 6 
 7 @MyAnnotation()
 8 public class Person {
 9 }
10     
11 public class Main {
12     public static void main(String[] args) {
13         // 通過反射獲取Person類的註解
14         Class<Person> clazz = Person.class;
15         MyAnnotation annotation = clazz.getAnnotation(MyAnnotation.class);
16 
17         // 獲取註解的值
18         String value = annotation.value();
19         System.out.println(value); // baidu(輸出了預設值)
20     }
21 }

4、元註解

  JDK 的元註解用於修飾其他註解定義。除了上面自定義註解用到的兩個,JDK5.0提供了4個標準的元註解,分別是:@Documented、@Inherited、@Retention、@Target。
  @Retention:用於指定註解的生命週期,它包含一個RetentionPolicy列舉型別的成員變數。
  原始碼示例:

 1 @Documented
 2 @Retention(RetentionPolicy.RUNTIME)
 3 @Target(ElementType.ANNOTATION_TYPE)
 4 public @interface Retention {
 5     /**
 6      * Returns the retention policy.
 7      * @return the retention policy
 8      */
 9     RetentionPolicy value();
10 }
11     
12 public enum RetentionPolicy {
13     /**
14      * Annotations are to be discarded by the compiler.
15      */
16     SOURCE,
17 
18     /**
19      * Annotations are to be recorded in the class file by the compiler
20      * but need not be retained by the VM at run time.  This is the default
21      * behavior.
22      */
23     CLASS,
24 
25     /**
26      * Annotations are to be recorded in the class file by the compiler and
27      * retained by the VM at run time, so they may be read reflectively.
28      *
29      * @see java.lang.reflect.AnnotatedElement
30      */
31     RUNTIME
32 }

  原始碼中的英文註釋寫的很清楚,下面翻譯一下:
  RetentionPolicy.SOURCE:在原始檔中有效(即原始檔保留),編譯器直接丟棄這種策略的註釋。
  RetentionPolicy.CLASS:在class檔案中有效(即class保留),當執行 Java 程式時,JVM不會保留註解。這是預設值。
  RetentionPolicy.RUNTIME:在執行時有效(即執行時保留),當執行 Java 程式時,JVM 會保留註釋。只有宣告為RUNTIME生命週期的註解,才可以通過反射獲取該註釋。
  @Target:用於指定註解能用於修飾哪些程式元素,它包含一個ElementType列舉型別的成員變數。
  原始碼示例:

 1 @Documented
 2 @Retention(RetentionPolicy.RUNTIME)
 3 @Target(ElementType.ANNOTATION_TYPE)
 4 public @interface Target {
 5     ElementType[] value();
 6 }
 7 
 8 public enum ElementType {
 9     // 用於修飾類、介面,或者enum宣告
10     /** Class, interface (including annotation type), or enum declaration */
11     TYPE,
12     
13     // 用於修飾域
14     /** Field declaration (includes enum constants) */
15     FIELD,
16     
17     // 用於修飾方法
18     /** Method declaration */
19     METHOD,
20     
21     // 用於修飾引數
22     /** Formal parameter declaration */
23     PARAMETER,
24     
25     // 用於修飾構造器
26     /** Constructor declaration */
27     CONSTRUCTOR,
28     
29     // 用於修飾區域性變數
30     /** Local variable declaration */
31     LOCAL_VARIABLE,
32     
33     // 用於修飾註解
34     /** Annotation type declaration */
35     ANNOTATION_TYPE,
36     
37     // 用於修飾包
38     /** Package declaration */
39     PACKAGE,
40 
41     /**
42      * Type parameter declaration
43      *
44      * @since 1.8
45      */
46     TYPE_PARAMETER,
47 
48     /**
49      * Use of a type
50      *
51      * @since 1.8
52      */
53     TYPE_USE
54 }

  @Documented:用於指定被該註解修飾的類將被 javadoc 工具提取成文件。預設情況下,javadoc是不包括註解的。定義為Documented的註解必須設定Retention值為RUNTIME。
  @Inherited:被它修飾的註解將具有繼承性。如果某個類使用了被@Inherited 修飾的註解,則其子類將自動具有該註解。

三、JDK8的新特性

1、介紹

  Java 8對註解處理提供了兩點改進:可重複的註解及可用於型別的註解。此外,反射也得到了加強,在Java8中能夠得到方法引數的名稱。這會簡化標註在方法引數上的註解。

2、可重複註解

  程式碼示例:方式一,JDK8之前

 1 @Retention(RetentionPolicy.RUNTIME)
 2 @Target({ElementType.TYPE})
 3 public @interface MyAnnotation {
 4     String value() default "baidu";
 5 }
 6 
 7 @Retention(RetentionPolicy.RUNTIME)
 8 @Target({ElementType.TYPE})
 9 public @interface MyAnnotations {
10     MyAnnotation[] value();
11 }
12 
13 //@MyAnnotation("xiaoming")
14 //@MyAnnotation("xiaohong")
15 @MyAnnotations({@MyAnnotation("xiaoming"), @MyAnnotation("xiaohong")})
16 public class Person {
17     private int age;
18 
19     public void say() {
20     }
21 }

  程式碼示例:方式二,JDK8之後

 1 // 注:MyAnnotation的Target和Retention等元註解須與MyAnnotations相同
 2 @Repeatable(MyAnnotations.class)
 3 @Retention(RetentionPolicy.RUNTIME)
 4 @Target({ElementType.TYPE})
 5 public @interface MyAnnotation {
 6     String value() default "baidu";
 7 }
 8 
 9 @Retention(RetentionPolicy.RUNTIME)
10 @Target({ElementType.TYPE})
11 public @interface MyAnnotations {
12     MyAnnotation[] value();
13 }
14 
15 @MyAnnotation("xiaoming")
16 @MyAnnotation("xiaohong")
17 public class Person {
18     private int age;
19 
20     public void say() {
21     }
22 }

3、型別註解

  在Java 8之前,註解只能在宣告的地方使用。Java8開始,註解可以應用在任何地方。
  ElementType.TYPE_PARAMETER:表示該註解能寫在型別變數的宣告語句中(如:泛型宣告)。
  ElementType.TYPE_USE:表示該註解能寫在使用型別的任何語句中。
  程式碼示例:TYPE_PARAMETER

 1 @Retention(RetentionPolicy.RUNTIME)
 2 @Target({ElementType.TYPE, ElementType.TYPE_PARAMETER})
 3 public @interface MyAnnotation {
 4     String value() default "baidu";
 5 }
 6 
 7 @MyAnnotation
 8 public class Person<@MyAnnotation T> {
 9     private T age;
10 
11     public void say() throws RuntimeException {
12         List<String> list = new ArrayList<>();
13 
14         int num = (int) 10L;
15     }
16 }

  程式碼示例:TYPE_USE

 1 @Retention(RetentionPolicy.RUNTIME)
 2 @Target({ElementType.TYPE, ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
 3 public @interface MyAnnotation {
 4     String value() default "baidu";
 5 }
 6 
 7 @MyAnnotation
 8 public class Person<@MyAnnotation T> {
 9     private T age;
10 
11     public void say() throws @MyAnnotation RuntimeException {
12         List<@MyAnnotation String> list = new ArrayList<>();
13 
14         int num = (@MyAnnotation int) 10L;
15     }
16 }

相關文章