ch11_enum_and_annotation

小兵学习笔记發表於2024-09-13
  • 第11章 列舉和註解
    • 先看一個需求
    • 列舉
      • 列舉的二種實現方式
      • 自定義類實現列舉-應用案例
    • enum 關鍵字實現列舉-快速入門
      • enum 關鍵字實現列舉注意事項
    • enum 常用方法說明
    • enum 常用方法應用例項
    • enum 實現介面
    • 註解的理解
      • 基本的Annotation 介紹
    • 基本的Annotation 應用案例
      • @Override
      • @Deprecated
      • @SuppressWarnings
        • 屬性介紹以及說明
    • JDK 的元Annotation(元註解)
      • 元註解的種類
      • @Retention
      • @Target
      • @Documented
      • @Inherited

第11章 列舉和註解

先看一個需求

要求建立季節(Season) 物件,請設計並完成。

class Season{//類
    private String name;
    private String desc;//描述
    //構造器
    //getXX
    //setXX
}

對於季節而已,他的物件(具體值),是固定的四個,不會有更多。一般的類設計類的思路,不能體現季節是固定的四個物件。採用列舉類。

  • 季節的值是有限的幾個值(spring, summer, autumn, winter)
  • 只讀,不需要修改。

因為不能存在如下的情況:

autumn.setName("XXX"); // 修改了秋天的名字

列舉

  1. 列舉對應英文(enumeration, 簡寫enum)
  2. 列舉是一組常量的集合。
  3. 可以這裡理解:列舉屬於一種特殊的類,裡面只包含一組有限的特定的物件。

列舉的二種實現方式

  1. 自定義類實現列舉
  2. 使用enum 關鍵字實現列舉

自定義類實現列舉-應用案例

1.不需要提供setXxx方法,因為列舉物件值通常為只讀.

2.對列舉物件/屬性使用 final + static共同修飾,實現底層最佳化.(final 和 static 搭配使用可以不導致類載入,效率更高)

3.列舉物件名通常使用全部大寫,常量的命名規範.

4.列舉物件根據需要,也可以有多個屬性

package com.hspedu.enum_;

public class Enumeration02 {
    public static void main(String[] args) {
        System.out.println(Season.AUTUMN);
        System.out.println(Season.SPRING);
    }
}

//演示字定義列舉實現
class Season {//類
    private String name;
    private String desc;//描述

    //定義了四個物件, 固定.
    public static final Season SPRING = new Season("春天", "溫暖");
    public static final Season WINTER = new Season("冬天", "寒冷");
    public static final Season AUTUMN = new Season("秋天", "涼爽");
    public static final Season SUMMER = new Season("夏天", "炎熱");


    //1. 將構造器私有化,目的防止 直接 new
    //2. 去掉setXxx方法, 防止屬性被修改
    //3. 在Season 內部,直接建立固定的物件
    //4. 最佳化,可以加入 final 修飾符(static會導致類載入,防止這種情況, final 和 static 搭配使用可以不導致類載入,效率更高)
    private Season(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }

    public String getName() {
        return name;
    }

    public String getDesc() {
        return desc;
    }

    @Override
    public String toString() {
        return "Season{" +
                "name='" + name + '\'' +
                ", desc='" + desc + '\'' +
                '}';
    }
}

小結:

  1. 構造器私有化
  2. 本類內部建立一組物件[四個 春夏秋冬]
  3. 對外暴露物件(透過為物件新增public final static 修飾符)
  4. 可以提供get 方法,但是不要提供set

enum 關鍵字實現列舉-快速入門

package com.hspedu.enum_;

public class Enumeration03 {
    public static void main(String[] args) {
        System.out.println(Season2.AUTUMN);
        System.out.println(Season2.SUMMER);
    }
}
//演示使用enum關鍵字來實現列舉類
enum  Season2 {//類


    //定義了四個物件, 固定.
//    public static final Season SPRING = new Season("春天", "溫暖");
//    public static final Season WINTER = new Season("冬天", "寒冷");
//    public static final Season AUTUMN = new Season("秋天", "涼爽");
//    public static final Season SUMMER = new Season("夏天", "炎熱");
    //如果使用了enum 來實現列舉類
    //1. 使用關鍵字 enum 替代 class
    //2. public static final Season SPRING = new Season("春天", "溫暖") 直接使用
    //   SPRING("春天", "溫暖") 解讀 常量名(實參列表)
    //3. 如果有多個常量(物件), 使用 ,號間隔即可
    //4. 如果使用enum 來實現列舉,要求將定義常量物件,寫在前面
    //5. 如果我們使用的是無參構造器,建立常量物件,則可以省略 ()
    SPRING("春天", "溫暖"), WINTER("冬天", "寒冷"), AUTUMN("秋天", "涼爽"),
    SUMMER("夏天", "炎熱"), What();

    private String name;
    private String desc;//描述

    private Season2() {//無參構造器

    }

    private Season2(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }

    public String getName() {
        return name;
    }

    public String getDesc() {
        return desc;
    }

    @Override
    public String toString() {
        return "Season{" +
                "name='" + name + '\'' +
                ", desc='" + desc + '\'' +
                '}';
    }
}

enum 關鍵字實現列舉注意事項

  1. 當我們使用enum 關鍵字開發一個列舉類時,預設會繼承Enum 類, 而且是一個final 類,使用 javap 工具來演示。

  2. 傳統的public static final Season2 SPRING = new Season2("春天", "溫暖"); 簡化成SPRING("春天", "溫暖"), 這裡必須知道,它呼叫的是哪個構造器。

  3. 如果使用無參構造器建立列舉物件,則實參列表和小括號都可以省略。

  4. 當有多個列舉物件時,使用,間隔,最後有一個分號結尾。

  5. 列舉物件必須放在列舉類的行首。

enum 常用方法說明

說明:使用關鍵字enum 時,會隱式繼承Enum 類, 這樣我們就可以使用Enum 類相關的方法。

enum 常用方法應用例項

  1. toString:Enum 類已經重寫過了,返回的是當前物件名,子類可以重寫該方法,用於返回物件的屬性資訊。
  2. name:返回當前物件名(常量名),子類中不能重寫。
  3. ordinal:返回當前物件的位置號,預設從0 開始。
  4. values:返回當前列舉類中所有的常量。
  5. valueOf:將字串轉換成列舉物件,要求字串必須為已有的常量名,否則報異常!
  6. compareTo:比較兩個列舉常量,比較的就是編號(前減後編號)!
package com.hspedu.enum_;

public class EnumMethod {
    public static void main(String[] args) {
        //使用Season2 列舉類,來演示各種方法
        Season2 autumn = Season2.AUTUMN;

        //輸出列舉物件的名字
        System.out.println(autumn.name());
        //ordinal() 輸出的是該列舉物件的次序/編號,從0開始編號
        //AUTUMN 列舉物件是第三個,因此輸出 2
        System.out.println(autumn.ordinal());
        //從反編譯可以看出 values方法,返回 Season2[]
        //含有定義的所有列舉物件
        Season2[] values = Season2.values();
        System.out.println("===遍歷取出列舉物件(增強for)====");
        for (Season2 season: values) {//增強for迴圈
            System.out.println(season);
        }

        //valueOf:將字串轉換成列舉物件,要求字串必須為已有的常量名,否則報異常
        //執行流程
        //1. 根據你輸入的 "AUTUMN" 到 Season2的列舉物件去查詢
        //2. 如果找到了,就返回,如果沒有找到,就報錯
        Season2 autumn1 = Season2.valueOf("AUTUMN");
        System.out.println("autumn1=" + autumn1);
        System.out.println(autumn == autumn1); // T

        //compareTo:比較兩個列舉常量,比較的就是編號
        //1. 就是把 Season2.AUTUMN 列舉物件的編號 和 Season2.SUMMER列舉物件的編號比較
        //2. 看看結果
        /*
        原始碼:
        public final int compareTo(E o) {

            return self.ordinal - other.ordinal;
        }
        Season2.AUTUMN的編號[2] - Season2.SUMMER的編號[3]
         */
        System.out.println(Season2.AUTUMN.compareTo(Season2.SUMMER));

        //補充了一個增強for
//        int[] nums = {1, 2, 9};
//        //普通的for迴圈
//        System.out.println("=====普通的for=====");
//        for (int i = 0; i < nums.length; i++) {
//            System.out.println(nums[i]);
//        }
//        System.out.println("=====增強的for=====");
//        //執行流程是 依次從nums陣列中取出資料,賦給i, 如果取出完畢,則退出for
//        for(int i : nums) {
//            System.out.println("i=" + i);
//        }
    }
}

enum 實現介面

  1. 使用enum 關鍵字後,就不能再繼承其它類了,因為enum 會隱式繼承Enum,而 Java 是單繼承機制

  2. 列舉類和普通類一樣,可以實現介面,如下形式。

    enum 類名 implements 介面1,介面2{}
    
    package com.hspedu.enum_;
    
    public class EnumDetail {
        public static void main(String[] args) {
            Music.CLASSICMUSIC.playing();
        }
    }
    class A {
    
    }
    
    //1.使用enum關鍵字後,就不能再繼承其它類了,因為enum會隱式繼承Enum,而Java是單繼承機制
    //enum Season3 extends A {
    //
    //}
    //2.enum實現的列舉類,仍然是一個類,所以還是可以實現介面的.
    interface IPlaying {
        public void playing();
    }
    enum Music implements IPlaying {
        // 列舉類,CLASSICMUSIC相當於一個物件
        CLASSICMUSIC;
        @Override
        public void playing() {
            System.out.println("播放好聽的音樂...");
        }
    }
    

註解的理解

  1. 註解(Annotation)也被稱為後設資料(Metadata),用於修飾解釋包、類、方法、屬性、構造器、區域性變數等資料資訊。
  2. 和註釋一樣,註解不影響程式邏輯,但註解可以被編譯或執行,相當於嵌入在程式碼中的補充資訊。
  3. 在JavaSE 中,註解的使用目的比較簡單,例如標記過時的功能,忽略警告等。在JavaEE 中註解佔據了更重要的角色,例如用來配置應用程式的任何切面,代替java EE 舊版中所遺留的繁冗程式碼和XML 配置等。

基本的Annotation 介紹

使用Annotation 時要在其前面增加 @ 符號, 並把該Annotation 當成一個修飾符使用。用於修飾它支援的程式元素。

三個基本的Annotation:

  1. @Override: 限定某個方法,是重寫父類方法, 該註解只能用於方法。
  2. @Deprecated: 用於表示某個程式元素(類, 方法等)已過時。
  3. @SuppressWarnings: 抑制編譯器警告。

基本的Annotation 應用案例

@Override

@Override:限定某個方法,是重寫父類方法,該註解只能用於方法。

補充說明: 原始碼中@interface不是interface介面,是註解類是jdk5.0之後加入的,表示一個註解類。

1.@ Override 表示指定重寫父類的方法(從編譯層面驗證),如果父類沒有fly方法,則會報錯。

2.如果不寫@Override註解,而父類仍有public void fly,仍然構成重寫。

3.@Override只能修飾方法,不能修飾其它類,包,屬性等等

4.檢視@Override註解原始碼為@Target(ElementType.METHOD), 說明只能修飾方法

5.@Target是修飾註解的註解,稱為元註解。

@Deprecated

@Deprecated: 用於表示某個程式元素(類, 方法等)已過時

1.用於表示某個程式元素(類,方法等)已過時

2.可以修飾方法,類,欄位,包,引數等等

3.@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD,
PACKAGE,PARAMETER. TYPE})

4.@Deprecated的作用可以做到新舊版本的相容和過渡

@SuppressWarnings

@SuppressWarnings: 抑制編譯器警告

  1. unchecked是忽略沒有檢查的警告
  2. rawtypes是忽略沒有指定泛型的警告(傳參時沒有指定泛型的警告錯誤)
  3. unused是忽略沒有使用某個變數的警告錯誤
  4. @SuppressWarnings可以修飾的程式元素為,檢視@Target
  5. 生成@SupperssWarnings時,不用背,直接點選左側的黃色提示,就
    可以選擇(注意可以指定生成的位置)

屬性介紹以及說明

all,抑制所有警告
boxing,抑制與封裝/拆裝作業相關的警告
cast,抑制與強制轉型作業相關的警告
dep-ann,抑制與淘汰註釋相關的警告
deprecation,抑制與淘汰的相關警告
fallthrough,抑制與switch陳述式中遺漏break相關的警告
finally,抑制與未傳回finally區塊相關的警告
hiding,抑制與隱藏變數的區域變數相關的警告
incomplete-switch,抑制與switch陳述式(enum case)中遺漏專案相關的警告
javadoc,抑制與javadoc相關的警告
nls,抑制與非nls字串文字相關的警告
null,抑制與空值分析相關的警告
rawtypes,抑制與使用raw型別相關的警告
resource,抑制與使用Closeable型別的資源相關的警告
restriction,抑制與使用不建議或禁止參照相關的警告
serial,抑制與可序列化的類別遺漏serialVersionUID欄位相關的警告
static-access,抑制與靜態存取不正確相關的警告
static-method,抑制與可能宣告為static的方法相關的警告
super,抑制與置換方法相關但不含super呼叫的警告
synthetic-access,抑制與內部類別的存取未最佳化相關的警告
sync-override,抑制因為置換同步方法而遺漏同步化的警告
unchecked,抑制與未檢查的作業相關的警告
unqualified-field-access,抑制與欄位存取不合格相關的警告
unused,抑制與未用的程式碼及停用的程式碼相關的警告

package com.hspedu.annotation_;

import java.util.ArrayList;
import java.util.List;
@SuppressWarnings({"rawtypes", "unchecked", "unused"})
public class SuppressWarnings_ {
    
    //1. 當我們不希望看到這些警告的時候,可以使用 SuppressWarnings註解來抑制警告資訊
    //2. 在{""} 中,可以寫入你希望抑制(不顯示)警告資訊
    //3. 關於SuppressWarnings 作用範圍是和你放置的位置相關
    //   比如 @SuppressWarnings放置在 main方法,那麼抑制警告的範圍就是 main
    //   通常我們可以放置具體的語句, 方法, 類.
    
    //4.  看看 @SuppressWarnings 原始碼
    //(1) 放置的位置就是 TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE
    //(2) 該註解類有陣列 String[] values() 設定一個陣列比如 {"rawtypes", "unchecked", "unused"}
    /*
        @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
            @Retention(RetentionPolicy.SOURCE)
            public @interface SuppressWarnings {

                String[] value();
        }
     */
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("jack");
        list.add("tom");
        list.add("mary");
        int i;
        System.out.println(list.get(1));

    }

    public void f1() {
//        @SuppressWarnings({"rawtypes"})
        List list = new ArrayList();


        list.add("jack");
        list.add("tom");
        list.add("mary");
//        @SuppressWarnings({"unused"})
        int i;
        System.out.println(list.get(1));
    }
}

JDK 的元Annotation(元註解)

JDK 的元Annotation 用於修飾其他 Annotation。

元註解的種類

  1. Retention //指定註解的作用範圍,三種SOURCE,CLASS,RUNTIME
  2. Target // 指定註解可以在哪些地方使用
  3. Documented //指定該註解是否會在javadoc 體現
  4. Inherited //子類會繼承父類註解

@Retention

只能用於修飾一個Annotation 定義, 用於指定該Annotation 可以保留多長時間, @Rentention 包含一個RetentionPolicy型別的成員變數, 使用@Rentention 時必須為該value 成員變數指定值: @Retention 的三種值

  1. RetentionPolicy.SOURCE: 編譯器使用後,直接丟棄這種策略的註釋
  2. RetentionPolicy.CLASS: 編譯器將把註解記錄在class 檔案中. 當執行Java 程式時, JVM 不會保留註解。這是預設值
  3. RetentionPolicy.RUNTIME:編譯器將把註解記錄在class 檔案中. 當執行Java 程式時, JVM 會保留註解. 程式可以透過反射獲取該註解。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE) // 編譯器編譯時生效,不會寫入到class檔案中
public @interface Override {
}

@Target

用於修飾 Annotation定義,用於指定被修飾的Annotation能用於修飾哪些程式元素.@Target 也包含一個名為value的成員變數。

@Documented
@Retention(RetentionPolicy.RUNTIME) // 作用範圍RUNTIME
@Target(ElementType.ANNOTATION_TYPE) // 只能修飾註解
public @interface Target { // 說明它是註解
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
    ElementType[] value(); // 這裡深入原始碼再看一下ElementType的取值
}
public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,

    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}

@Documented

@Documented:用於指定被該元 Annotation修飾的Annotation類將被javadoc工具提取成文件,即在生成文件時,可以看到該註解。

說明:定義為 Documented 的註解必須設定 Retention 值為 RUNTIME

@Inherited

被它修飾的Annotation將具有繼承性。如果某個類使用了被@Inherited修飾的Annotation,則其子類將自動具有該註解。