1. 程式人生 > 其它 >JavaSE列舉類與註解 2021.07.07-10

JavaSE列舉類與註解 2021.07.07-10

JavaSE列舉類與註解 2021.07.07-10

勇敢牛牛,不怕困難!

列舉類

  • 類的物件只有有限個,確定的,稱為列舉類。
  • 當需要定義一組常量時,強烈建議使用列舉類
  • 如果列舉類中只有一個物件,則可以作為單例模式的實現方式。

定義列舉類

方式一:jdk5.0 之前,自定義列舉類
方式二:jdk5.0 之後,可以使用enum關鍵字定義列舉類

自定義列舉類:

public class SeasonTest {
    public static void main(String[] args) {
        System.out.println(Season.SUMMER);//Season{seasonName='夏天', seasonDecs='夏日炎炎'}
    }
}

class Season{
    //1.宣告Season的屬性:private final修飾
    private final String seasonName;
    private final String seasonDecs;

    //2.私有化類的構造器,並給物件屬性賦值
    private Season(String seasonName,String seasonDecs){
        this.seasonName=seasonName;
        this.seasonDecs=seasonDecs;
    }
    
    //3.提供當前列舉類的多個物件:public static final
    public static final Season SPRING = new Season("春天","春暖花開");
    public static final Season SUMMER = new Season("夏天","夏日炎炎");
    public static final Season AUTUMN = new Season("秋天","秋高氣爽");
    public static final Season WINTER = new Season("冬天","冰天雪地");

    //4.其他訴求1:獲取列舉類物件的屬性
    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDecs() {
        return seasonDecs;
    }

    //5.其他訴求2:提供toString()
    @Override
    public String toString() {
        return "Season{" +
                "seasonName='" + seasonName + '\'' +
                ", seasonDecs='" + seasonDecs + '\'' +
                '}';
    }
}

使用enum關鍵字定義列舉類:

public class SeasonTest1 {
    public static void main(String[] args) {
        Season1 summer = Season1.SUMMER;
        System.out.println(summer);//SUMMER,因為父類Enum重寫了toString(),返回物件(常量)的名字
        System.out.println(Season1.class.getSuperclass());//class java.lang.Enum,定義的列舉類預設繼承於java.lang.Enum類
    }
}

enum Season1{
    //1.提供當前列舉類的物件,多個物件之間用","隔開,末尾物件用";"結束
    SPRING("春天","春暖花開"),
    SUMMER("夏天","夏日炎炎"),
    AUTUMN("秋天","秋高氣爽"),
    WINTER("冬天","冰天雪地");

    //2.宣告Season1物件的屬性:private final修飾
    private final String seasonName;
    private final String seasonDecs;

    //3.私有化類的構造器,並給物件屬性賦值
    private Season1(String seasonName,String seasonDecs){
        this.seasonName=seasonName;
        this.seasonDecs=seasonDecs;
    }

    //4.其他訴求1:獲取列舉類物件的屬性
    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDecs() {
        return seasonDecs;
    }
}

Enum類的常用方法

  • values():返回列舉型別的物件陣列。該方法可以很方便地遍歷所有的列舉值
  • valueOf():可以把一個字串轉為對應的列舉類物件。要求字串必須是列舉類物件的“名字”。如不是,會有執行時異常:IllegalArgumentException
  • toString():返回當前列舉類物件常量的名稱
        //values():
        Season1[] values = Season1.values();
        for (int i = 0; i < values.length; i++) {
            System.out.println(values[i]);
        }
/*
SPRING
SUMMER
AUTUMN
WINTER
*/

        Thread.State[] values1 = Thread.State.values();
        System.out.println(Arrays.toString(values1));//[NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED]

        //valueOf(String objName):返回列舉類中物件名是objName的物件
        Season1 spring = Season1.valueOf("SPRING");
        System.out.println(spring);//SPRING
        //Season1 spring1 = Season1.valueOf("SPRING1");//如果沒有objName對應的列舉類物件,則拋異常:IllegalArgumentException

使用關鍵字enum定義的列舉類實現介面

  • 情況一:實現介面,在enum類中實現抽象方法
    @Override
    public void show() {
        System.out.println("這是一個季節");
    }
        //main
        Season1 spring = Season1.valueOf("SPRING");
        System.out.println(spring);
        spring.show();//這是一個季節
  • 情況二:讓列舉類的物件分別實現介面中的抽象方法
    SPRING("春天","春暖花開"){
        @Override
        public void show() {
            System.out.println("春天來了");
        }
    },
    SUMMER("夏天","夏日炎炎"){
        @Override
        public void show() {
            System.out.println("夏天來了");
        }
    },
    AUTUMN("秋天","秋高氣爽"){
        @Override
        public void show() {
            System.out.println("秋天來了");
        }
    },
    WINTER("冬天","冰天雪地"){
        @Override
        public void show() {
            System.out.println("冬天來了");
        }
    };
         //main
        Season1[] values = Season1.values();
        for (int i = 0; i < values.length; i++) {
            System.out.println(values[i]);
            values[i].show();
        }
        /*
        SPRING
        春天來了
        SUMMER
        夏天來了
        AUTUMN
        秋天來了
        WINTER
        冬天來了
        */

註解(Annotation)

  • jdk 5.0 新增的功能
  • Annotation 其實就是程式碼裡的特殊標記,這些標記可以在編譯,類載入,執行時被讀取,並執行相應的處理。通過使用Annotation,程式設計師可以在不改變原有邏輯的情況下,在原始檔中嵌入一些補充資訊
  • 在JavaSE中,註解的使用目的比較簡單,例如標記過時的功能,忽略警告等。在JavaEE/Android中註解佔據了更重要的角色,例如用來配置應用程式的任何切面,代替JavaEE舊版中所遺留的繁冗程式碼和XML配置等。
  • 一定程度上,框架 = 註解 + 反射 + 設計模式

示例

一、生成文件相關的註解

  • @author 標明開發該類模組的作者,多個作者之間使用","分隔

  • @version 標明該類模組的版本

  • @see 參考轉向,也就是相關主題

  • @since 從哪個版本開始增加的

  • @param 對方法中某引數的說明,如果沒有引數就不能寫

  • @return 對方法返回值的說明,如果方法的返回值型別是void就不能寫

  • @exception 對方法可能丟擲的異常進行說明,如果方法沒有用throws顯式丟擲的異常就不能寫

  • 其中

    • @param @return @exception 這三個標記都是隻用於方法的
    • @param 的格式要求:@param 形參名 形參型別 形參說明
    • @return 的格式要求 :@return 返回值型別 返回值說明
    • @exception 的格式要求:@exception 異常型別 異常說明
    • @param 和@exception可以並列多個

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

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

三、跟蹤程式碼依賴性,實現替代配置檔案功能


自定義註解

參照@SuppressWarnings定義

  • 註解宣告為@interface
  • 內部定義成員,通常使用value()表示
  • 可以指定成員的預設值,使用default定義
  • 如果自定義註解沒有成員,表明是一個標識作用
  • 如果註解有成員,在使用註解時,需要指明成員的值。
  • 自定義註解必須配上註解的資訊處理流程(使用反射)才有意義。
  • 自定義註解通常都會指明兩個元註解:Retention、Target
public @interface MyAnnotation {
    String value() /*default "hello" 指定預設值*/ ;
}

//使用
@MyAnnotation(/*value =*/ "hello")

自定義註解自動繼承java.lang.annotation.Annotation介面
Annotation的成員變數在定義中以無參方法的形式來宣告。其方法名和返回值定義了該成員的名字和型別,稱為配置引數。型別只能是八種基本型別、String型別、Class型別、enum型別、Annotation型別、以上所有型別的陣列。


jdk提供的4種元註解

元資料:對現有的資料進行解釋說明的資料

比如 String name = "beckham";beckham是資料的話,String name 就是元資料

元註解:對現有的註解進行解釋說明的註解

@Retention

指定所修飾的Annotation的生命週期:

包含一個RetetionPolicy型別(列舉類)的成員變數,使用時必須為該value成員變數指定值:

  • RetentionPolicy.SOURCE:在原始檔中有效(原始檔保留,java檔案)編譯器直接丟棄這種註釋。
  • RetentionPolicy.CLASS:在class檔案中有效(class保留,java檔案經過編譯後得到class檔案,即javac.exe命令(編譯)),當執行Java程式時,JVM不會保留註釋。這是預設值
  • Retention.RUNTIME:在執行時有效(執行時保留),當執行Java程式時,JVM會保留註釋。只有宣告為RUNTIME生命週期的註解,才能通過反射獲取

@Target

用於指定被修飾的 Annotation 能用於修飾哪些程式元素

包含一個名為ElementType(列舉類)的成員變數

@Documented(使用頻率低)

  • 表示所修飾的註解在被 javadoc 解析時,保留下來。預設情況下,javadoc不保留註解。
  • 定義為Documented的註解必須設定Retention值為RUNTIME

@Inherited(使用頻率低)

  • 被修飾的Annotation具有繼承性

通過反射獲取註解資訊(暫時瞭解一下)

//驗證Inherited 的繼承性	
        //main
        Class studentClass = Student.class;
        Annotation[] annotations = studentClass.getAnnotations();
        for (int i = 0; i < annotations.length; i++) {
            System.out.println(annotations[i]);//@com.annotationTest.MyAnnotation(value=hello)
        }
        
@MyAnnotation(/*value =*/ "hello")
class Person{
}

class Student extends Person{
}

jdk 8.0 中註解的新特性

可重複註解

jdk 8之前的寫法:

//
public @interface MyAnnotation {
    String value() /*default "hello"*/ ;
}

public @interface MyAnnotations {
    MyAnnotation[] value();
}

//使用重複註解,新建註解MyAnnotations,定義MyAnnotation陣列
@MyAnnotations({@MyAnnotation(value = "hello"),@MyAnnotation(value = "hi")})

jdk 8 之後的寫法:

  • 在MyAnnotation上宣告@Repeatable,成員值為MyAnnotations.class
  • MyAnnotation的Target和Retention等元註解 與 MyAnnotations的相同
@Repeatable(MyAnnotations.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
public @interface MyAnnotation {
    String value() /*default "hello"*/ ;
}

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
public @interface MyAnnotations {
    MyAnnotation[] value();
}

//使用重複註解
@MyAnnotation(value = "hello")
@MyAnnotation(value = "hi")

型別註解

  • jdk 8之後,元註解@Target的引數型別ElementType列舉值多了兩個:TYPE_PARAMETER,TYPE_USE
  • jdk 8開始,註解可以應用在任何地方。
    • ElementType.TYPE_PARAMETER 表示該註解能寫在型別變數的宣告語句中(如:泛型宣告)
    • ElementType.TYPE_USE 表示該註解能寫在使用型別的任何語句中。
class Teacher <@MyAnnotation T>{ //ElementType.TYPE_PARAMETER,ElementType.TYPE_USE 都可以修飾
    public void show() throws @MyAnnotation RuntimeException{ //ElementType.TYPE_USE

        ArrayList<@MyAnnotation String> list = new ArrayList(); //ElementType.TYPE_USE

        int num = (@MyAnnotation int)10L; //ElementType.TYPE_USE
    }
}

@Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD,ElementType.TYPE_PARAMETER,ElementType.TYPE_USE})
public @interface MyAnnotation {
    String value() default "hello" ;
}