21-列舉與註解
阿新 • • 發佈:2020-07-12
列舉型別
- 有時候,變數(物件) 的取值只在一個有限的集合內。比如:
- 星期:Monday(星期一)、...、Sunday(星期天)
- 性別:Man(男)、Woman(女)
- 季節:Spring(春節)、Summer(夏天),Autumn(秋天),Winter(冬天)
- 支付方式:Cash(現金)、WeChatPay(微信)、Alipay(支付寶)、BankCard(銀行卡)、CreditCard(信用卡)
- 就職狀態:Busy、Free、Vocation、Dimission
- 訂單狀態:Nonpayment(未付款)、Paid(已付款)、Delivered(已發貨)、 Return(退貨)、Checked(已確認)、Fulfilled(已配貨)
- 執行緒狀態:建立、就緒、執行、阻塞、死亡
- 針對這種情況,可以自定義列舉型別。列舉型別包括有限個的物件。
- JDK5.0 之前需要自定義列舉類
- JDK5.0 新增的
enum
用於定義列舉類
- 若列舉只有一個物件, 則可以作為一種 [單例模式] 的實現方式
- 當需要定義一組常量時,強烈建議使用列舉類
定義列舉類
自定義列舉類
- 列舉類物件的屬性不應允許被改動,所以應該使用
private final
修飾 private final
修飾的屬性應該在 [構造器] 中為其賦值- 若列舉類顯式的定義了帶引數的構造器,則在列出列舉值時也必須對應的傳入引數
- 私有化類的構造器,保證不能在類的外部建立其物件
- 在類的內部建立列舉類的例項。宣告為:
public static final
// JDK5.0 之前,自定義列舉類 class Season { // 1. 宣告 Season 物件的屬性(private final) private final String seasonName; private final String seasonDesc; // 2. 私有化類的構造器 private Season(String seasonName, String seasonDesc) { this.seasonName = seasonName; this.seasonDesc = seasonDesc; } // 3. 提供當前列舉類的多個物件 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 getSeasonDesc() { return seasonDesc; } // 5. [訴求2] 提供 toString() @Override public String toString() { return "Season{" + "seasonName='" + seasonName + '\'' + ", seasonDesc='" + seasonDesc + '\'' + '}'; } }
使用 enum 定義列舉類
- 使用
enum
定義的列舉類預設繼承了java.lang.Enum
類,因此不能再繼承其他類 - 列舉類的構造器只能使用
private
許可權修飾符 - 列舉類的所有例項必須在列舉類中顯式列出(',' 分隔 & ';' 結尾) 列出的
- 例項系統會自動新增
public static final
修飾 // 所以你不要多此一舉 - 必須在列舉類的第一行宣告列舉類物件
// JDK5.0 之後,使用 enum 關鍵字定義列舉類 (預設繼承於 Java.lang.Enum)
enum Season1 { // extends Enum<Season1>
// 1. 提供當前列舉類的物件,物件之間用 "," 隔開
SPRING("春天", "春暖花開"), SUMMER("夏天", "夏日炎炎"),
AUTUMN("秋天", "秋高氣爽"), WINTER("冬天", "冰天雪地");
// 2. 宣告 Season 物件的屬性(private final)
private final String seasonName;
private final String seasonDesc;
// 3. 私有化類的構造器
private Season1(String seasonName, String seasonDesc) {
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
// 4. [訴求] 獲取列舉類物件的屬性
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
// toString(): 列印引用名
}
JDK5.0 中可以在 switch 表示式中使用 Enum 定義的列舉類的物件作為表示式,case 子句可以直接使用列舉值的名字,無需新增列舉類作為限定。
Enum類
- 使用
enum
定義的列舉類預設繼承了java.lang.Enum
類 - Enum 類的主要方法
values()
:返回列舉型別的物件陣列。該方法可以很方便地遍歷所有的列舉值
valueOf(String str)
:可以把一個字串轉為對應的列舉類物件。要求字串必須是列舉類物件常量名。如不是,會丟擲執行時異常:IllegalArgumentException
toString()
:返回當前列舉類物件常量的名稱
實現介面的列舉類
- 實現介面,在 enum 類中實現抽象方法 // 每個物件呼叫此方法,執行相同的方法體
- 讓 enum 類中的物件分別實現介面中的抽象方法
interface Info {
void func();
}
enum Season1 implements Info { // extends Enum<Season1>
SPRING("春天", "春暖花開") {
@Override
public void func() {
System.out.println("春天的味道");
}
},
SUMMER("夏天", "夏日炎炎") {
@Override
public void func() {
System.out.println("夏天的味道");
}
},
AUTUMN("秋天", "秋高氣爽") {
@Override
public void func() {
System.out.println("秋天的味道");
}
},
WINTER("冬天", "冰天雪地") {
@Override
public void func() {
System.out.println("冬天的味道");
}
};
/*
@Override
public void func() {
System.out.println("季節是有氣味的");
}
希望每個物件呼叫func(),展現出不同的內容 → 每個物件各自實現func()
*/
// ...
}
註解
概述
- 從 JDK 5.0 開始, Java 增加了對 元資料(MetaData) 的支援, 也就是 Annotation(註解)
- Annotation 其實就是程式碼裡的特殊標記,這些標記可以在編譯、類載入、執行時被讀取,並執行相應的處理。通過使用 Annotation,程式設計師可以在不改變原有邏輯的情況下,在原始檔中嵌入一些補充資訊。程式碼分析工具、開發工具和部署工具可以通過這些補充資訊進行驗證或者進行部署
- Annotation 可以像修飾符一樣被使用,可用於修飾包、類、構造器、方法、成員變數、引數、區域性變數的宣告,這些資訊被儲存在 Annotation 的 "name=value" 對中
- 在 JavaSE中,註解的使用目的比較簡單,例如標記過時的功能, 忽略警告等。在 JavaEE/Android 中註解佔據了更重要的角色,例如 用來配置應用程式的任何切面,代替 JavaEE 舊版中所遺留的繁冗 程式碼和XML配置等
- 未來的開發模式都是基於註解的,JPA 是基於註解的,Spring2.5 以上都是基於註解的,Hibernate3.x 以後也是基於註解的,現在的 Struts2 有一部分也是基於註解的了。註解是一種趨勢。一定程度上可以說:框架 = 註解 + 反射 + 設計模式
常見的Annotation示例
使用 Annotation 時要在其前面增加 @
符號, 並把該 Annotation 當成 一個修飾符使用,用於修飾它支援的程式元素。
- [示例1] 生成文件相關的註解
- [示例2] 在編譯時進行格式檢查(JDK內建的三個基本註解)
@Override
限定重寫父類方法, 該註解只能用於方法@Deprecated
用於表示所修飾的元素(類, 方法等)已過時。通常是因為所修飾的結構危險或存在更好的選擇@SuppressWarnings
抑制編譯器警告
- [示例3] 跟蹤程式碼依賴性,實現替代配置檔案功能
- Servlet3.0 提供了註解(annotation),使得不再需要在 web.xml 檔案中進行 Servlet 的部署
- spring 框架中關於“事務”的管理
- Servlet3.0 提供了註解(annotation),使得不再需要在 web.xml 檔案中進行 Servlet 的部署
自定義 Annotation
自定義註解必須配上註解的資訊處理流程(反射)才有意義
- 定義新的 Annotation 型別使用
@interface
關鍵字 - 自定義註解自動繼承了
java.lang.annotation.Annotation<I>
- Annotation 的成員變數 在 Annotation 定義中以無引數方法的形式來宣告,其方法名和返回值定義了該成員的名字和型別,我們稱為 [配置引數]。型別只能是 8 種基本資料型別、String型別、Class型別、enum型別、Annotation型別及以上所有型別的陣列
- 可以在定義 Annotation 的成員變數時為其指定初始值, 指定成員變數的初始值可使用
default
關鍵字 - 如果只有一個引數成員,建議使用引數名為 "value"
- 如果定義的註解含有配置引數,那麼使用時必須指定引數值,除非它有預設值。格式是 "引數名 = 引數值",如果只有一個引數成員,且名稱為 value, 可以省略 "value="
- 沒有成員定義的 Annotation 稱為 [標記];包含成員變數的 Annotation 稱為 [元資料Annotation]
public @interface MyAnnotation {
String value() default "Hello";
}
元註解
JDK 的元Annotation 用於修飾其他 Annotation 定義。JDK5.0提供了4個標準的 meta-annotation 型別,分別是:Retention
,Target
,Documented
,Inherited
- @Retention
- 只能用於修飾一個 Annotation 定義,用於指定該 Annotation 的生命週期,
@Rentention
包含一個RetentionPolicy
列舉型別的成員變數 - 使用
@Rentention
時必須為該名為value
成員變數指定值RetentionPolicy.SOURCE
:在原始檔中有效(即原始檔保留),編譯器直接丟棄這種策略的註釋RetentionPolicy.CLASS
:在 class 檔案中有效(即class保留),當執行 Java 程式時,JVM 不會保留註解。 這是預設值RetentionPolicy.RUNTIME
:在執行時有效(即執行時保留),執行 Java 程式時, JVM 會 保留註釋。程式可以通過反射獲取該註釋
- 圖示
- 只能用於修飾一個 Annotation 定義,用於指定該 Annotation 的生命週期,
- @Target
- 用於修飾 Annotation 定義,用於指定被修飾的 Annotation 能用於修飾哪些程式元素。@Target 包含一個
ElementType
列舉型別的成員變數 - 使用
@Rentention
時必須為該名為value
成員變數指定值
- 用於修飾 Annotation 定義,用於指定被修飾的 Annotation 能用於修飾哪些程式元素。@Target 包含一個
- @Documented
- 用於指定被該 元Annotation 修飾的 Annotation 類將被 javadoc 工具提取成文件
- 預設情況下,javadoc 是不包括註解的
- 定義為 Documented 的註解必須設定 Retention 值為 RUNTIME
- @Inherited
- 被它修飾的 Annotation 將具有繼承性
- 如果某個類使用了被
@Inherited
修飾的 Annotation,則其子類將自動具有該註解 - 如果把標有
@Inherited
註解的自定義的註解標註在類級別上,子類則可以繼承父類類級別的註解
可重複註解、型別註解
JDK8 對註解處理提供了兩點改進:可重複的註解及可用於型別的註解。此外,反射也得到了加強,在 JDK8 中能夠得到方法引數的名稱。這會簡化標註在方法引數上的註解。
可重複的註解
在 JDK8 之前我們不能在一個型別重複使用同一個註解;如果想要這樣使用,要用陣列來實現重複註解。
public @interface MyAnnotation {
String value();
}
public @interface MyAnnotations {
MyAnnotation[] value();
}
-------------- 使用 ↓ ----------------
@MyAnnotations({@MyAnnotation("Hello"), @MyAnnotation("World")})
class Person {}
JDK8 之後,在需要重複的註解上宣告@Repeatable,成員值為 MyAnnotations.class,讓這 2 個註解關聯在一起。並且,MyAnnotation 的 @Target
、@Retention
等元註解要與 MyAnnotations 一致。
@Repeatable(MyAnnotations.class)
public @interface MyAnnotation {
String value();
}
public @interface MyAnnotations {
MyAnnotation[] value();
}
-------------- 使用 ↓ ----------------
@MyAnnotation("World")
@MyAnnotation("Hello")
class Person {}
型別註解
JDK8 之後,關於元註解 @Target
的引數型別 ElementType
列舉值多了 2 個:TYPE_PARAMETER
,TYPE_USE
。在 JDK8 之前,註解只能是在宣告的地方所使用;JDK8 開始,註解可以應用在任何地方。
ElementType.TYPE_PARAMETER
:表示該註解能寫在型別變數的宣告語句中(如:泛型宣告)
ElementType.TYPE_USE
:表示該註解能寫在使用型別的任何語句中