当前位置:首页 » 《休闲阅读》 » 正文

Java 枚举

8 人参与  2024年11月13日 14:41  分类 : 《休闲阅读》  评论

点击全文阅读


目录

枚举是什么

常用方法

构造方法

枚举的优缺点

枚举和反射

实现单例模式


枚举是什么

枚举(enum):是一种特殊的类,用于定义一组常量,将其组织起来。枚举使得代码更具有可读性和可维护性,特别是在处理固定集合的值时,如:星期、月份、状态码等

在 Java 中,使用关键字 enum 来定义枚举类:

public enum Day {    SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY;}

其中,定义的枚举项就是该类的实例,且必须在第一行,最后一个枚举项后的分号; 可以省略,但是若枚举类有其他内容,则分号不能省略(最好不要省略) 

当类初始化时,这些枚举项就会被实例化

枚举类使用 enum 定义后,默认继承 java.lang.Enum 类,也就是说,我们自己写的枚举类,就算没有显示的继承 Enum,但是其默认继承了这个类

此外,枚举在 Java 中不能被继承,自定义的枚举类隐式继承自 java.lang.Enum 类,且不能再继承其他类,这样的设计确保了枚举类的简单性和一致性。如果枚举可以继承其他类,将会导致复杂的继承关系,并且影响Java的类型系统

常用方法

方法描述
values()以数组的形式返回枚举类型的所有成员

ordinal()

获取枚举成员的索引位置
valueOf()将普通字符串转换为枚举实例

compareTo(E o)

比较两个枚举成员在定义时的顺序

 我们通过一个示例,来学习和使用这些方法:

public enum Day {    SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY;    public static void main(String[] args) {        // 获取所有枚举成员        Day[] days = Day.values();        // 遍历        for (int i = 0; i < days.length; i++) {            // 获取枚举成员以及索引位置            System.out.println(days[i] + " " + days[i].ordinal());        }        // 将普通字符串转换为枚举实例        System.out.println(Day.valueOf("THURSDAY"));        // 获取枚举实例 SUNDAY 和 SATURDAY        Day sunday = Day.SUNDAY;        Day saturday = Day.SATURDAY;        // 比较定义时的顺序        System.out.println(sunday.compareTo(saturday));    }}

运行结果:

在使用 valueOf() 方法进行转换时,传递的名称必须与枚举常量的名字完全匹配(包括大小写),若不匹配,就会抛出 IllegalArgumentException 异常:

 当我们查看 java.lang.Enum 时:

可以看到,valueOf() 方法包含了两个类型的参数:Class<T> enumClass  和  String name

其中

Class<T> enumType 是一个 Class 对象,表示要查找的枚举类型

String name: 是一个字符串,表示要查找的枚举常量的名称。名称必须与枚举常量的名字完全匹配(包括大小写)

 但是在使用时,我们只传递了一个参数 name,也能够进行转换,这是为什么呢?

这是因为,在 Java 中,valueOf 方法实际上是自动生成的属于每个枚举类型的特性。虽然它的原始定义需要两个参数(类类型和名称),但是每个枚举类型都会自动提供一个与自身类型相关联的 valueOf 方法,只需传递一个字符串参数

因此,当我们调用 Day.valueOf("THURSDAY") 时,Java会自动处理这个调用,实际调用的是包含类名的 valueOf 方法,而不是原始的静态方法定义

再观察 java.lang.Enum:

我们会发现,其中并不存在 values() 这个方法,而当我们点击 values() 方法时,则会跳转到本类上

那么,values() 方法是从哪来的呢?

values() 方法是枚举类自动提供的一个静态方法,允许我们获取一个包含所有枚举常量的数组,这个方法是由Java编译器自动生成的,在编译时每个枚举类型都会自动生成一个 values() 方法,因此不需要我们显式定义它

我们将枚举类进行反编译:

(1)打开 cmd,切换到 Day.java 文件所在目录

(2)编译 .java 文件(javac Day.java)

(3)将 .class 文件进行反编译(javap -c Day.class > day.txt)

打开 day.txt,可以看到:

编译器自动为我们生成了 values 和 valueOf 方法

构造方法

当我们创建构造方法时:

 不能使用 public 来修饰构造方法,为什么呢?

这是因为,在 Java 中,枚举类的构造方法都是私有的(不加任何修饰符时,默认是 private),无法在枚举类外部调用,这也就防止了在枚举类外部创建新的枚举常量

在定义枚举常量时,构造方法会被隐式调用:

public enum Day {    SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY;    Day() {        System.out.println("构造方法");    }    public static void main(String[] args) {        System.out.println("------------------");    }}

运行结果: 

 当我们定义带有参数的构造方法时,在创建枚举项时,也要为其提供对应参数:

public enum Day {    SUNDAY("周天", 7),    MONDAY("周一", 1),    TUESDAY("周二", 2),    WEDNESDAY("周三", 3),    THURSDAY("周四", 4),    FRIDAY("周五", 5),    SATURDAY("周六", 6);    private String name;    private int key;    Day(String name, int key) {        this.name = name;        this.key = key;    }}

枚举的优缺点

优点:

(1)枚举常量确保了只能使用定义的常量,避免了使用整型常量时可能带来的错误

(2)使用枚举可以使代码更易读,表达清晰

(3)枚举定义了一组固定的常量,适合表示有限的状态或选项,便于管理和维护

(4)枚举可以拥有字段、方法和构造方法,能够封装与常量相关的行为和属性

(5)Java的枚举类自带一些方法,如 values()、valueOf() 等

(6)可以用于 switch 语句

缺点:

(1)枚举的集合是固定的,无法在运行时添加或删除常量。如果需要动态的集合,枚举可能不适用

(2)枚举不能继承,无法扩展

(3)每个枚举常量都是一个对象,可能会增加内存使用,特别是当枚举常量数量较多时

枚举和反射

在 Java 反射-CSDN博客 中,我们学习了反射,通过反射,我们可以拿到类的私有构造方法,从而创建实例对象

那么,枚举是否可以通过反射,拿到实例对象呢?

public enum Day {    SUNDAY("周天", 7),    MONDAY("周一", 1),    TUESDAY("周二", 2),    WEDNESDAY("周三", 3),    THURSDAY("周四", 4),    FRIDAY("周五", 5),    SATURDAY("周六", 6);    private String name;    private int key;    Day(String name, int key) {        this.name = name;        this.key = key;    }}
public class Test {    public static void main(String[] args) {        Class<?> classDay = null;        try {            classDay = Class.forName("Day");            Constructor<?> constructor = classDay.getDeclaredConstructor(String.class, int.class);            constructor.setAccessible(true);            Day day = (Day) constructor.newInstance("sunday", 0);            System.out.println(day.ordinal());        } catch (ClassNotFoundException e) {            throw new RuntimeException(e);        } catch (NoSuchMethodException e) {            throw new RuntimeException(e);        } catch (InvocationTargetException e) {            throw new RuntimeException(e);        } catch (InstantiationException e) {            throw new RuntimeException(e);        } catch (IllegalAccessException e) {            throw new RuntimeException(e);        }    }}

运行结果:

此时程序抛出了 NoSuchMethodException 异常,也就是没有对应的构造方法

但是提供的枚举的构造方法就是带有两个参数,分别为 String 和 int:

那么,问题出在哪里呢?

自定义的枚举类默认继承自 java.lang.Enum

因此,自定义的枚举类继承了父类除构造方法外的所有东西,且子类需要帮助父类进行构造,

但我们实现的类中,并没有帮助父类进行构造

因此,我们需要在枚举类中帮助父类进行构造,而父类中的构造方法为:

那么,如何实现呢?通过 super 方法吗?

但是,当我们在构造方法中调用 super 时:

枚举构造方法中不能使用 super

 由于枚举比较特殊,在构造方法中,除了我们自定义了两个参数,它还默认添加了父类的两个参数

也就是说,构造函数中一共有四个参数:String int String int

其中,前两个参数是父类参数,后两个参数是子类参数

public class Test {    public static void main(String[] args) {        Class<?> classDay = null;        try {            classDay = Class.forName("enumDemo.Day");            Constructor<?> constructor = classDay.getDeclaredConstructor(String.class, int.class, String.class, int.class);            constructor.setAccessible(true);            Day day = (Day) constructor.newInstance("父类参数", 0, "子类参数", 0);            System.out.println(day.ordinal());        } catch (ClassNotFoundException e) {            throw new RuntimeException(e);        } catch (NoSuchMethodException e) {            throw new RuntimeException(e);        } catch (InvocationTargetException e) {            throw new RuntimeException(e);        } catch (InstantiationException e) {            throw new RuntimeException(e);        } catch (IllegalAccessException e) {            throw new RuntimeException(e);        }    }}

再次运行:

此时抛出了 IllegalArgumentException 异常,不能通过反射创建枚举对象

枚举保证了每个枚举常量只有一个实例,这种唯一性在枚举类型被定义时就已经确定,不允许外部创建新的实例,枚举类型的设计使得它们的实例在类加载时被唯一地定义,从而避免了通过反射创建新的枚举实例的可能性,确保了枚举的强类型安全性和唯一性

实现单例模式

在 单例模式:饿汉模式、懒汉模式_单例模式懒汉和饿汉-CSDN博客 中我们实现了单例模式,单例模式能够确保一个类只有一个实例

但普通类可以通过反射机制打破,因此,我们可以使用枚举来实现单例模式

public enum Singleton {    INSTANCE;    public Singleton getInstance() {        return INSTANCE;    }}

点击全文阅读


本文链接:http://m.zhangshiyu.com/post/185589.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1