Java 枚舉

一、簡介

Java 枚舉是一種強大的工具,其本質上是一個繼承自 java.lang.Enum 的類,用于定義一組固定的常量,每個枚舉常量都是該枚舉類的一個實例。枚舉不僅提供了類型安全性,還可以像普通類一樣擁有字段、方法和構造函數。枚舉的使用場景非常廣泛,包括表示一組相關的常量、實現單例模式等。通過合理使用枚舉,可以使代碼更加清晰、安全和易于維護。

1.1 枚舉的基本語法

枚舉通過 enum 關鍵字定義,通常包含一組常量。枚舉常量通常用大寫字母表示,多個常量之間用逗號分隔。

  • 每個枚舉常量都是枚舉類型的一個實例。
  • 枚舉常量默認是 public static final 的,因此可以直接通過枚舉類名訪問。
public enum Day {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
  • 編譯后的枚舉類結構
    • 上述枚舉代碼會被編譯器轉換為類似以下的普通類:
      public final class Day extends Enum<Day> {// 枚舉常量public static final Day MONDAY = new Day("MONDAY", 0);public static final Day TUESDAY = new Day("TUESDAY", 1);public static final Day WEDNESDAY = new Day("WEDNESDAY", 2);public static final Day THURSDAY = new Day("THURSDAY", 3);public static final Day FRIDAY = new Day("FRIDAY", 4);public static final Day SATURDAY = new Day("SATURDAY", 5);public static final Day SUNDAY = new Day("SUNDAY", 6);// 枚舉常量數組private static final Day[] $VALUES = new Day[] {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY};// 私有構造函數private Day(String name, int ordinal) {super(name, ordinal);}// values() 方法public static Day[] values() {return $VALUES.clone();}// valueOf() 方法public static Day valueOf(String name) {return Enum.valueOf(Day.class, name);}
      }
      

二、枚舉的用法

2.1 枚舉的常用方法

Java 枚舉類默認繼承自 java.lang.Enum,因此可以使用以下常用方法:

  1. values()
    • 返回枚舉類型的所有常量,返回一個數組。
      Day[] days = Day.values();
      for (Day day : days) {System.out.println(day);
      }
      
  2. valueOf(String name)
    • 根據名稱返回對應的枚舉常量。如果名稱不存在,會拋出 IllegalArgumentException。
      Day day = Day.valueOf("MONDAY");
      System.out.println(day); // 輸出: MONDAY
      
  3. name()
    • 返回枚舉常量的名稱(字符串形式)。
      Day day = Day.MONDAY;
      System.out.println(day.name()); // 輸出: MONDAY
      
  4. ordinal()
    • 返回枚舉常量的序號(從 0 開始)。
      Day day = Day.MONDAY;
      System.out.println(day.ordinal()); // 輸出: 0
      

三、枚舉的特性

3.1 枚舉是類

雖然枚舉看起來像是一組常量,但實際上每個枚舉常量都是枚舉類的一個實例。枚舉類可以像普通類一樣擁有字段、方法和構造函數。

  • 枚舉的構造函數必須是私有的(private),因為枚舉常量是在枚舉類內部定義的。
  • 每個枚舉常量在定義時會調用構造函數,并傳入相應的參數。
public enum Day {MONDAY("星期一", 1),TUESDAY("星期二", 2),WEDNESDAY("星期三", 3),THURSDAY("星期四", 4),FRIDAY("星期五", 5),SATURDAY("星期六", 6),SUNDAY("星期日", 7);private final String chineseName;private final int dayNumber;// 枚舉的構造函數必須是私有的private Day(String chineseName, int dayNumber) {this.chineseName = chineseName;this.dayNumber = dayNumber;}public String getChineseName() {return chineseName;}public int getDayNumber() {return dayNumber;}
}
3.2 枚舉的方法重寫

枚舉常量可以重寫枚舉類中的方法。

  • 在下面這個例子中,Day 枚舉類定義了一個抽象方法 getActivity(),每個枚舉常量都實現了這個方法。
public enum Day {MONDAY {@Overridepublic String getActivity() {return "Work";}},SATURDAY {@Overridepublic String getActivity() {return "Relax";}},SUNDAY {@Overridepublic String getActivity() {return "Relax";}};public abstract String getActivity();
}
3.3 枚舉的靜態方法

枚舉類可以定義靜態方法,這些方法可以通過枚舉類名直接調用。

public enum Day {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;public static Day fromString(String day) {return Day.valueOf(day.toUpperCase());}
}
3.4 枚舉實現接口

枚舉類可以實現接口,從而為枚舉常量提供統一的行為。

public interface Activity {String getActivity();
}public enum Day implements Activity {MONDAY {@Overridepublic String getActivity() {return "Work";}},SATURDAY {@Overridepublic String getActivity() {return "Relax";}};@Overridepublic abstract String getActivity();
}
3.5 枚舉的單例模式

由于枚舉常量是唯一的,枚舉類型可以用來實現單例模式。

public enum Singleton {INSTANCE;public void doSomething() {System.out.println("Doing something");}
}

四、枚舉的實現原理

Java 枚舉的實現原理是基于類的繼承和靜態字段的單例模式。枚舉常量是枚舉類的實例,通過私有構造函數創建,并在類加載時初始化。

  1. 枚舉類的繼承關系
    • 枚舉類默認繼承自 java.lang.Enum,因此不能顯式繼承其他類(Java 不支持多繼承)。
    • Enum 類實現了 Comparable 和 Serializable 接口,因此枚舉常量可以比較大小,并且可以被序列化。
  2. 枚舉常量的創建
    • 每個枚舉常量都是枚舉類的一個實例,在類加載時通過靜態代碼塊初始化。
    • 枚舉常量的創建是通過調用枚舉類的私有構造函數完成的。
  3. 枚舉常量的唯一性
    • 枚舉常量是單例的,每個常量在 JVM 中只有一個實例。
    • 枚舉常量的唯一性是通過私有構造函數和靜態字段實現的。
  4. 枚舉的方法
    • values() 方法:返回枚舉類的所有常量,返回一個數組。
    • valueOf() 方法:根據名稱返回對應的枚舉常量。
    • name() 和 ordinal() 方法:分別返回枚舉常量的名稱和序號。

五、枚舉的底層實現細節

  1. 枚舉的構造函數
    • 枚舉的構造函數必須是私有的(private),因為枚舉常量是在枚舉類內部定義的。
    • 枚舉常量的創建是通過調用私有構造函數完成的。
  2. 枚舉常量的存儲
    • 枚舉常量存儲在靜態字段中,這些字段是 public static final 的。
    • 枚舉常量數組 $VALUES 存儲了所有的枚舉常量。
  3. 枚舉的線程安全性
    • 枚舉常量的創建是在類加載時完成的,因此是線程安全的。
    • 枚舉的單例模式天然支持線程安全。

六、枚舉的編譯優化

6.1 枚舉的 switch 語句優化

在 switch 語句中使用枚舉時,編譯器會將枚舉轉換為 ordinal() 值進行比較,從而提高性能。

Day day = Day.MONDAY;
switch (day) {case MONDAY:System.out.println("It's Monday");break;case TUESDAY:System.out.println("It's Tuesday");break;default:System.out.println("It's another day");
}

上述代碼會被編譯器轉換為類似以下的代碼:

int ordinal = day.ordinal();
switch (ordinal) {case 0:System.out.println("It's Monday");break;case 1:System.out.println("It's Tuesday");break;default:System.out.println("It's another day");
}

七、枚舉的序列化

Java 枚舉的序列化機制是基于名稱的,具有唯一性、安全性和高效性。枚舉的序列化和反序列化過程由 JVM 自動處理,開發者無需額外實現。

  1. 枚舉的序列化機制
    Java 枚舉的序列化機制是基于其名稱(name)的,而不是基于字段或狀態。具體來說:

    • 序列化:枚舉實例會被序列化為它的名稱(name)。
    • 反序列化:通過名稱查找對應的枚舉實例。

    這種機制確保了枚舉的唯一性和單例特性。

  2. 枚舉序列化的特點

    • 唯一性:枚舉實例在 JVM 中是單例的,序列化和反序列化不會破壞這種唯一性。
    • 安全性:枚舉的序列化機制是安全的,不會因為反序列化創建新的實例。
    • 不可變性:枚舉的字段通常是不可變的(final),因此序列化不會影響其狀態。
  3. 枚舉序列化的示例

    • 定義一個枚舉
      public enum Color {RED, GREEN, BLUE
      }
      
    • 序列化枚舉
      import java.io.*;public class EnumSerializationExample {public static void main(String[] args) {// 序列化try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("color.ser"))) {oos.writeObject(Color.RED);System.out.println("枚舉序列化完成");} catch (IOException e) {e.printStackTrace();}}
      }
      
    • 反序列化枚舉
      import java.io.*;public class EnumDeserializationExample {public static void main(String[] args) {// 反序列化try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("color.ser"))) {Color color = (Color) ois.readObject();System.out.println("反序列化的枚舉: " + color);} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}}
      }
      

八、枚舉的注意事項

  1. 枚舉常量是單例的:每個枚舉常量在 JVM 中只有一個實例。
  2. 枚舉的構造函數是私有的:不能顯式調用枚舉的構造函數。
  3. 枚舉不能被繼承:枚舉類默認是 final 的,不能被其他類繼承。
  4. 枚舉可以實現接口:但不能繼承其他類,因為枚舉類默認繼承自 java.lang.Enum。

九、枚舉的使用場景

9.1 表示一組固定的常量

枚舉最常見的用途是表示一組固定的常量,例如星期、月份、狀態等。

public enum Status {PENDING, APPROVED, REJECTED
}
9.2 在 switch 語句中使用

枚舉常量可以與 switch 語句一起使用。

Day day = Day.MONDAY;switch (day) {case MONDAY:System.out.println("It's Monday");break;case TUESDAY:System.out.println("It's Tuesday");break;default:System.out.println("It's another day");
}
9.3 實現單例模式

由于枚舉常量是唯一的,枚舉類型可以用來實現單例模式。

public enum Singleton {INSTANCE;public void doSomething() {System.out.println("Doing something");}
}// 使用單例
Singleton.INSTANCE.doSomething();
9.4 枚舉集合

Java 提供了專門的集合類 EnumSet 和 EnumMap,用于高效地操作枚舉類型。

EnumSet<Day> weekend = EnumSet.of(Day.SATURDAY, Day.SUNDAY);
System.out.println(weekend.contains(Day.SATURDAY)); // 輸出: trueEnumMap<Day, String> activities = new EnumMap<>(Day.class);
activities.put(Day.MONDAY, "Work");
activities.put(Day.SATURDAY, "Relax");
System.out.println(activities.get(Day.MONDAY)); // 輸出: Work

十、常見問題

10.1 為什么說枚舉是實現單例的最好方式
  1. 枚舉天然是單例
    • 單例特性:枚舉的每個實例在 JVM 中是唯一的,且枚舉的構造器是私有的,無法通過 new 關鍵字創建新的實例。
    • 全局唯一:枚舉實例在類加載時被初始化,并且在整個 JVM 生命周期內保持唯一。
  2. 線程安全
    • 線程安全:枚舉的實例化過程由 JVM 保證線程安全,無需開發者手動實現同步機制。
  3. 防止反射攻擊
    • 反射安全:傳統的單例實現方式(如私有構造器)可以通過反射機制破壞單例特性,而枚舉的構造器在底層被 JVM 特殊處理,無法通過反射創建新的實例。
  4. 防止反序列化破壞單例
    • 序列化安全:傳統的單例實現方式在反序列化時可能會創建新的實例,而枚舉的序列化機制是基于名稱的,反序列化時會返回相同的實例,確保單例的唯一性。
  5. 代碼簡潔
    • 簡潔性:枚舉實現單例的代碼非常簡潔,無需手動處理線程安全、序列化等問題。
    • 可讀性:枚舉的單例實現方式清晰易懂,符合 Java 的最佳實踐。
10.2 為什么接口返回值不能使用枚舉類型
  1. 枚舉類型的局限性
    枚舉類型是一種特殊的類,它的值是固定的(在編譯時確定),并且無法動態擴展。這種特性在某些場景下會限制接口的靈活性。

    public enum Status {SUCCESS, FAILURE
    }public interface Service {Status performAction();
    }
    
    • 如果將來需要新增狀態(如 PENDING),必須修改枚舉定義并重新編譯代碼。
  2. 破壞接口的開放性
    接口的設計原則之一是 開閉原則(Open/Closed Principle),即對擴展開放,對修改封閉。使用枚舉作為返回值可能會破壞這一原則:

    • 無法擴展:枚舉的值是固定的,無法在運行時動態擴展。
    • 耦合性高:客戶端代碼需要依賴具體的枚舉類型,增加了耦合性。
    public enum Status {SUCCESS, FAILURE
    }public interface Service {Status performAction();
    }// 客戶端代碼
    public class Client {public void handleResponse(Status status) {switch (status) {case SUCCESS:System.out.println("Success");break;case FAILURE:System.out.println("Failure");break;default:throw new IllegalArgumentException("Unknown status");}}
    }
    
    • 如果新增 PENDING 狀態,客戶端代碼必須修改 switch 語句。
  3. 不利于多態性
    枚舉類型是具體的類型,無法通過繼承擴展。如果接口返回值使用枚舉類型,會限制多態性的發揮。

    public enum Status {SUCCESS, FAILURE
    }public interface Service {Status performAction();
    }// 無法擴展 Status
    public enum ExtendedStatus extends Status { // 編譯錯誤PENDING
    }
    
  4. 序列化問題
    雖然枚舉天然支持序列化,但在分布式系統或跨語言調用(如 RESTful API)中,枚舉的序列化可能會帶來兼容性問題:

    • 跨語言支持差:其他語言可能不支持枚舉類型,導致反序列化失敗。
    • 版本兼容性差:如果枚舉類型發生變化(如新增值),舊版本的客戶端可能無法正確處理。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/73199.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/73199.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/73199.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

CentOS7安裝DNS服務器bind

文章目錄 安裝DNS服務設置配置文件自定義域名解析完整配置 需求是公司內網服務器無法連接外網&#xff0c;需要在本地搭建DNS服務&#xff0c;這樣物理機器遷移到內網后&#xff0c;通過域名解析訪問服務 DNS服務器 172.25.14.215 ip域名172.25.14.216mysql.server172.25.14.2…

DFS刷題(25.3.13)

題目1——烤雞 題目描述 題解 這是一個簡單的暴搜題目&#xff0c;由于一共由10種配料&#xff0c;每種配料可以放1到3克&#xff0c;因此只需要用dfs對每種配料放入的質量進行暴力搜索即可&#xff0c;如果放入的配料質量之和等于題目給出的美味程度 n n n&#xff0c;記錄一…

C#中除了Dictionary,List,HashSet,HashTable 還有哪些可以保存列表的數據類型?

在 C# 中&#xff0c;除了 Dictionary、List、HashSet 和 Hashtable 之外&#xff0c;還有許多其他可以保存列表或集合類型的數據結構&#xff0c;具體包括以下幾類&#xff1a; &#x1f4cc; 數組類 1. Array&#xff08;數組&#xff09; 固定長度&#xff0c;性能高&…

《Python實戰進階》第21集:數據存儲:Redis 與 MongoDB 的使用場景

第21集&#xff1a;數據存儲&#xff1a;Redis 與 MongoDB 的使用場景 摘要 在現代應用開發中&#xff0c;數據存儲的選擇直接影響系統的性能、擴展性和成本。Redis 和 MongoDB 是兩種極具代表性的數據庫技術&#xff0c;它們分別擅長解決不同場景下的問題。本文將深入探討 Re…

三視圖轉stl導出 空心面片體 networkx shapely triangle numpy-stl

from shapely.geometry import Polygon import triangle from shapely.ops import unary_union from stl import mesh import numpy as np from collections import defaultdict from 三維投影線段尋找 import get_adjusted_clusters,get_clusters,get_intersect_lines import …

大摩閉門會:250312 學習總結報告

如果圖片分辨率不足&#xff0c;可右鍵圖片在新標簽打開圖片或者下載末尾源文件進行查看 本文只是針對視頻做相應學術記錄&#xff0c;進行學習討論使用

【51單片機】程序實驗15.DS18B20溫度傳感器

主要參考學習資料&#xff1a;B站【普中官方】51單片機手把手教學視頻 開發資料下載鏈接&#xff1a;http://www.prechin.cn/gongsixinwen/208.html 單片機套裝&#xff1a;普中STC51單片機開發板A4標準版套餐7 目錄 DS18B20介紹主要特性內部結構控制時序初始化時序寫時序讀時序…

ESP32芯片模組方案,設備物聯網無線通信,WiFi藍牙交互控制應用

在當下&#xff0c;物聯網正以前所未有的速度席卷全球&#xff0c;從繁華都市的智能建筑&#xff0c;到寧靜鄉村的智慧農業&#xff0c;從人們日常使用的可穿戴設備&#xff0c;到工業領域復雜精密的自動化生產線&#xff0c;物聯網的觸角已深入到生活與生產的每一個角落。 而…

Linux第二次練習

1.首先在根下面創建一個名為text的目錄 2.在根目錄下新建一個text目錄&#xff0c;然后在text目錄中新建上圖的一級目錄、二級目錄以及三級目錄 3.顯示/text目錄下文件的樹形拓撲圖 4.將linux樹狀結構圖中列出的所有文件用ll命令列出來

百雞問題-

百雞問題 #include<stdio.h> int main(){int n;scanf("%d",&n);int x,y,z;for(x0;x<100;x){for(y0;y<100;y){for(z0;z<100;z){if((x*15y*9z)<(3*n) && ((xyz)100)){printf("x%d,y%d,z%d\n",x,y,z);}}}}return 0; }

LVDS(Low Voltage Differential Signaling)電平詳解

一、LVDS的定義與核心特性 LVDS&#xff08;低壓差分信號&#xff09;是一種 低功耗、高速、抗干擾 的差分信號傳輸技術&#xff0c;通過一對互補的電壓信號&#xff08;正負端差值&#xff09;傳遞數據。其核心特性包括&#xff1a; 電氣特性 電壓擺幅&#xff1a;差分電壓約…

【OpenFeign 面試專題】

OpenFeign 面試專題 OpenFeign 的核心原理OpenFeign 如何與 Ribbon、Hystrix 集成Ribbon的負載均衡策略如何自定義 OpenFeign 的請求編碼和響應解碼OpenFeign 如何傳遞請求頭&#xff08;Header&#xff09;信息OpenFeign 如何處理超時和重試OpenFeign 支持哪些 HTTP 客戶端實現…

Adobe Acrobat Pro setting

防火墻斷網組織彈窗 Adobe軟件突然彈窗“THIS APP HAS BEEN DISABLED”&#xff1f;別慌&#xff0c;幾步教你輕松解決&#xff01; 禁用代理 解決Adobe出現This unlicensed Photoshop app has been disabled.禁止使用 rules:- DOMAIN-KEYWORD,adobe,REJECT

搜索插入位置(js實現,LeetCode:35)

給定一個排序數組和一個目標值&#xff0c;在數組中找到目標值&#xff0c;并返回其索引。如果目標值不存在于數組中&#xff0c;返回它將會被按順序插入的位置。 請必須使用時間復雜度為 O(log n) 的算法。 示例 1: 輸入: nums [1,3,5,6], target 5 輸出: 2示例 2: 輸入…

5. 前后端實現文件上傳與解析

1. 說明 在實際開發中&#xff0c;比較常見的一個功能是需要在前端頁面中選擇系統中的某個文件上傳到服務器中進行解析&#xff0c;解析后的文件內容可以用來在服務器中當作參數&#xff0c;或者傳遞給其它組件使用&#xff0c;或者需要存儲到數據庫中。所以本文就提供一種方式…

《靈珠覺醒:從零到算法金仙的C++修煉》卷三·天劫試煉(32)萬劍歸宗破妖陣 - 最長遞增子序列(LIS)

《靈珠覺醒:從零到算法金仙的C++修煉》卷三天劫試煉(32)萬劍歸宗破妖陣 - 最長遞增子序列(LIS) 哪吒在數據修仙界中繼續他的修煉之旅。這一次,他來到了一片神秘的萬劍谷,谷中有一座巨大的萬劍歸宗劍陣,劍陣閃爍著神秘的光芒。谷口有一塊巨大的石碑,上面刻著一行文字:…

【redis】使用redis作為緩存時所注意事項

緩存更新策略 在 Redis 緩存中&#xff0c;緩存的更新策略主要有**定期生成&#xff08;定時更新&#xff09;和實時生成&#xff08;即時更新&#xff09;**兩種方式。不同的策略適用于不同的業務場景&#xff0c;涉及性能、數據一致性和系統負載等方面的權衡。 1. 定期生成&…

計算機網絡:計算機網絡的分類

按分布范圍分類&#xff1a;廣域網&#xff0c;城域網&#xff0c;局域網&#xff0c;個域網 按傳輸技術分類&#xff1a;廣播式網絡&#xff0c;點對點網絡 按拓撲結構分類&#xff1a;總線型&#xff0c;環形&#xff0c;星形&#xff0c;網狀 按傳輸介質分類&#xff1a;…

解決pip安裝uv時下載速度慢

驗證優化效果 方案 1&#xff1a;臨時使用國內鏡像源&#xff08;推薦&#xff09; pip install uv -i https://pypi.tuna.tsinghua.edu.cn/simple 速度提升&#xff1a;鏡像源服務器位于國內&#xff0c;帶寬充足&#xff0c;通常可達 1-10MB/s 支持源列表&#xff1a; # 清…

SpringCloud Alibaba——入門簡介

一、是什么 &#xff08;1&#xff09;誕生 2018.10.31&#xff0c;Spring Cloud Alibaba 正式入駐了 Spring Cloud 官方孵化器&#xff0c;并在 Maven 中央庫發布了第一個版本 &#xff08;2&#xff09;介紹 &#xff08;3&#xff09;&#xff1f;何為必須組件 二、能干嘛…