哪些网站可以做企业推广,文件夹里内容做网站的分类,想制作自己的网站,wordpress 发表评论目录 枚举
引入
定义
代码示例
常用方法
代码示例
枚举的优缺点
枚举和反射
面试题 枚举
引入
枚举是在JDK1.5以后引入的。主要用途是#xff1a;将一组常量组织起来#xff0c;在这之前表示一组常量通常使用定义常量的方式#xff1a; publicstaticintfinalRED1;…
目录 枚举
引入
定义
代码示例
常用方法
代码示例
枚举的优缺点
枚举和反射
面试题 枚举
引入
枚举是在JDK1.5以后引入的。主要用途是将一组常量组织起来在这之前表示一组常量通常使用定义常量的方式 publicstaticintfinalRED1; publicstaticintfinalGREEN2; publicstaticintfinalBLACK3; 但是常量举例有不好的地方例如可能碰巧有个数字1但是他有可能误会为是RED现在我们可以直接用枚举来进行组织这样一来就拥有了类型枚举类型。而不是普通的整形1。
定义 在Java中枚举类型是通过关键字enum来定义的。 枚举的定义类似于类的定义但它使用enum关键字而不是class关键字。枚举可以包含字段、方法和构造函数但构造函数默认是私有的以防止外部代码创建枚举的实例。 本质是 java.lang.Enum的子类也就是说自己写的枚举类就算没有显示继承 Enum但是它默认继承了这个类。 publicenumTestEnum{RED,BLACK,GREEN;
}
代码示例
public enum TestEnum {//枚举对象RED,WHITE,GREEN;public static void main(String[] args) {TestEnum testEnum TestEnum.RED;switch (testEnum) {case RED:System.out.println(红色);break;case GREEN:System.out.println(绿色);break;case WHITE:System.out.println(白色);break;default:break;}}}
运行结果 常用方法
方法名称描述values()以数组形式返回枚举类型的所有成员ordinal()获取枚举成员的索引位置valueOf()将普通字符串转换为枚举类型compareTo()比较两个枚举类型成员在定义时的顺序 当枚举对象有参数后需要提供相应的构造函数枚举的构造函数默认是私有的。 代码示例
public enum TestEnum {
// 枚举对象RED(1,RED),WHITE(2,WHITE),GREEN(3,GREEN);public String color;public int ordinal;private TestEnum(int ordinal,String color) {this.ordinal ordinal;this.color color;}public static void main(String[] args) {TestEnum[] testEnums TestEnum.values();for (int i 0; i testEnums.length; i) {System.out.println(testEnums[i] testEnums[i].ordinal());}System.out.println();TestEnum testEnum TestEnum.valueOf(RED);System.out.println(testEnum);System.out.println(RED.compareTo(WHITE));}
}
运行结果 枚举的优缺点
优点 1.类型安全枚举类型是编译时常量它们比使用整数值或字符串常量更加安全。 2.自动封装枚举类型提供了编译时的类型检查确保了只有声明在枚举中的值才能被赋值给枚举类型的变量。 3.可以包含字段和方法枚举类型可以拥有字段、方法和构造函数。 4.构造器限制枚举的构造函数默认是私有的防止外部代码实例化枚举。 5.实现接口枚举类型可以实现一个或多个接口。 缺点 1.不可变性限制Java中的枚举实例默认是不可变的即枚举值一旦创建其状态就不能改变。这种不可变性在大多数情况下是一个优点因为它有助于保证线程安全和简化代码逻辑。然而在某些情况下你可能希望枚举值能够改变其状态但这在Java枚举中是不被允许的。虽然你可以通过枚举中的方法改变枚举关联的其他对象的状态但这通常不是最佳实践。 2.继承限制Java中的枚举类型隐式地继承自java.lang.Enum类并且Java不支持多重继承。因此枚举类型不能继承自除java.lang.Enum之外的任何其他类。这限制了枚举的灵活性并可能导致一些设计上的折衷。 枚举和反射
枚举是否能通过反射拿到实例对象呢
代码示例
public enum TestEnum {
// 枚举对象RED(1,RED),WHITE(2,WHITE),GREEN(3,GREEN);public String color;public int ordinal;private TestEnum(int ordinal,String color) {this.ordinal ordinal;this.color color;}public static void main(String[] args) throws ClassNotFoundException {Class? c Class.forName(enumDemo.TestEnum);try {Constructor? constructor c.getDeclaredConstructor(int.class,String.class);constructor.setAccessible(true);TestEnum testEnum (TestEnum)constructor.newInstance(99,hello);System.out.println(testEnum);} 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);}}
} 运行结果 我们注意到异常信息是java.lang.NoSuchMethodException: enumDemo.TestEnum.init(int, java.lang.String)。
这是什么意思呢就是没有对应的构造方法但是我们明明已经提供了枚举的构造方法且两个参数分别是 int 和 String那么问题出在哪里呢
前面我们提到所有的枚举类都默认继承于 java.lang.Enum继承了父类除构造函数之外的所有内容并且子类要帮助父类进行构造而我们写的类并没有帮助父类构造那是否意味着我们要在实现的枚举类中提供 super 呢
并不是枚举类比较特殊虽然我们实现的构造函数是两个参数但是它默认还添加了两个参数那添加的两个参数是什么呢下面我们看一下Enum类的部分源码 也就是说我们自己的构造函数有两个参数一个是int一个是String同时他默认在此前面还会给两个参数一个是String一个是int。也就是说这里我们正确给的是4个参数修改后的代码
public static void main(String[] args) throws ClassNotFoundException {Class? c Class.forName(enumDemo.TestEnum);try {Constructor? constructor c.getDeclaredConstructor(String.class,int.class,int.class,String.class);constructor.setAccessible(true);TestEnum testEnum (TestEnum)constructor.newInstance(ceshi,999,99,hello);System.out.println(testEnum);} 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);}
}
运行结果 为什么此时在newInstance()方法会抛出异常 java.lang.IllegalArgumentException: Cannot reflectively create enum objects。
下面我们看看 newInstance() 方法的源码 解释 在这段代码中if ((clazz.getModifiers() Modifier.ENUM) ! 0) 这一行是用来检查给定的类clazz是否是一个枚举Enum类型。 这里使用了位运算和位掩码的概念来检查类的修饰符中是否包含了枚举Enum的修饰符。在Java中每个类都可以有一组修饰符这些修饰符定义了类的性质比如是否是公开的public、私有的private、受保护的protected、抽象的abstract、最终的final等以及是否是枚举enum。这些修饰符在内部是通过整数通常是int类型的位模式来表示的每个修饰符都对应一个特定的位位置。 Modifier.ENUM 是一个在 java.lang.reflect.Modifier 类中定义的常量它代表了枚举类型的位掩码。这个常量是一个整数其位模式中的某一位或几位但在这个上下文中通常只是一位被设置为1以表示枚举类型。 clazz.getModifiers() 方法返回了一个整数这个整数包含了clazz类的所有修饰符的位模式。 是按位与AND运算符它对两个整数进行操作并返回一个新的整数这个整数的每一位都是原来两个整数对应位进行AND操作的结果。如果两个整数在某一位上都是1则结果在该位上也是1否则结果在该位上是0。 因此clazz.getModifiers() Modifier.ENUM 的结果是一个整数它只在clazz的修饰符中包含枚举类型修饰符时才不为0。如果结果不为0说明clazz是一个枚举类型如果结果为0说明clazz不是一个枚举类型。 所以if ((clazz.getModifiers() Modifier.ENUM) ! 0) 这行代码的意思是“如果clazz是一个枚举类型则执行接下来的代码块在这个例子中是抛出一个IllegalArgumentException异常”。这是因为在Java中你不能通过反射来实例化枚举类型的对象因为枚举的实例通常是通过枚举类型本身定义的常量来访问的而不是通过构造函数创建的。 面试题
为什么枚举实现单例是线程安全的 1.自动线程安全Java的枚举类型是自动支持线程安全的。因为枚举类型本质上是通过类的静态字段来实现的并且JVM保证了每个枚举实例在JVM中是唯一的。这意味着在并发环境下枚举实例的创建和访问都是线程安全的无需额外的同步措施。 2.防止反射攻击Java的枚举还有一个重要的特性那就是它们默认是final的并且枚举的构造函数默认也是私有的。这意味着枚举的实例不能被继承也不能通过反射来调用枚举的构造函数来创建新的实例尽管技术上可以通过反射调用私有构造函数但Java平台禁止通过反射实例化枚举如果尝试这样做会抛出IllegalArgumentException或IllegalAccessException异常。这种限制确保了枚举实例的唯一性和不可变性从而增强了单例模式的安全性。 3.自动序列化机制枚举类型还提供了自动的序列化机制。当枚举实例被序列化时Java序列化机制仅仅是将枚举的name属性即枚举常量的名称输出到序列化流中而不是像普通对象那样序列化其状态。反序列化时Java通过枚举的name来查找枚举类型中对应的枚举常量从而恢复枚举实例。这个机制确保了枚举实例在序列化和反序列化过程中仍然保持单例。 4.简单性和易用性使用枚举实现单例模式非常简单只需要定义一个枚举类型并在其中定义一个枚举常量即可。这种实现方式既简洁又直观易于理解和维护。 综上所述枚举实现单例模式之所以被认为是安全的主要是因为它提供了自动的线程安全、防止了反射攻击、支持自动序列化机制并且实现简单、易用。这些特性使得枚举成为实现单例模式的理想选择之一。