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.mzph.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;何为必须组件 二、能干嘛…