Java 的枚举是一个特殊的类,一般表示一组常量,比如一年的4季、一年的12个月份,一星期的7天,方向有东南西北等等。类似这种当一个变量有几种固定可能的取值时,就可以将它定义为枚举类型
定义形式:
修饰符 enum 枚举名:基础类型 {
枚举成员,
}
示例代码: 用枚举类来定义季节
public enum Season {
spring,summer,fall,winter;
}
补充:
java.lang.Enum
类(虽然没有显示继承),并实现了 java.lang.Seriablizable
和 java.lang.Comparable
两个接口public static final
的,且非抽象的枚举类不能再派生子类在 Java 中,switch(表达式)
中的表达式是有类型限制的
它只能使用:整数(只包括 byte、short、int)、字符(char)、字符串(String)、枚举类型
switch语句 表达式使用枚举示例:
public enum Season {
spring,summer,fall,winter;
public static void main(String[] args) {
Season season=Season.summer;
switch(season){
case spring:
System.out.println("春季");
break;
case summer:
System.out.println("夏季");
break;
case fall:
System.out.println("秋季");
break;
case winter:
System.out.println("冬季");
break;
default:
break;
}
}
}
// 结果为:夏季
方法 | 说明 |
---|---|
values() | 以数组的形式返回枚举类型的所有成员 |
ordinal() | 获取枚举成员的索引位置 |
valueOf() | 返回指定字符串值的枚举常量,不存在会报 IllegalArgumentException 异常 |
compareTo() | 比较两个枚举成员在定义时的顺序 |
示例1: 以数组的形式返回枚举类型的所有成员
public enum Season {
spring,summer,fall,winter;
public static void main(String[] args) {
Season[] seasons=Season.values();
for(Season s: seasons){
System.out.print(s+" ");
}
}
}
// 结果为:spring summer fall winter
示例2: 获取枚举成员的索引位置
public enum Season {
spring,summer,fall,winter;
public static void main(String[] args) {
Season[] seasons=Season.values();
for(Season s: seasons){
System.out.println(s.ordinal()+": "+s);
}
}
}
/** 结果为:
0: spring
1: summer
2: fall
3: winter
*/
示例3: 返回指定字符串值的枚举常量
public enum Season {
spring,summer,fall,winter;
public static void main(String[] args) {
System.out.println(Season.valueOf("spring"));
System.out.println(Season.valueOf("Monday"));
}
}
示例4: 比较两个枚举成员在定义时的顺序
public enum Season {
spring,summer,fall,winter;
public static void main(String[] args) {
System.out.println(spring.compareTo(summer));
System.out.println(spring.compareTo(winter));
}
}
// 结果为:-1 -3
当定义一个枚举类型时,每一个枚举类型成员都可以看作是 Enum 类的实例,即枚举值是一个对象
如果枚举对象有参数后,则需要提供相应的构造方法,且这个构造方法要是私有的
示例:
public enum Season {
spring("春季"),summer("夏季"),fall("秋季"),winter("冬季");
public String name;
private Season(String name){
this.name=name;
}
public static void main(String[] args) {
Season[] seasons=Season.values();
for(Season s: seasons){
System.out.println(s.name);
}
}
}
// 结果为:春季 夏季 秋季 冬季
定义一个 Operation 枚举类,它有4个枚举值:plus、min、mul、div,分别代表加减乘除。并且该枚举类有一个 calculate() 方法用于计算
示例:
public enum Operation {
plus{
@Override
public double calculate(double a, double b) {
return a+b;
}
},
min{
@Override
public double calculate(double a, double b) {
return a-b;
}
},
mul{
@Override
public double calculate(double a, double b) {
return a*b;
}
},
div{
@Override
public double calculate(double a, double b) {
return a/b;
}
};
public abstract double calculate(double a,double b);
public static void main(String[] args) {
System.out.println(Operation.plus.calculate(20,10));
System.out.println(Operation.min.calculate(20,10));
System.out.println(Operation.mul.calculate(20,10));
System.out.println(Operation.div.calculate(20,10));
}
}
// 结果为:30.0 10.0 200.0 2.0
由于枚举的构造方法是私有的,因此不能直接获取到枚举类的实例。那么通过反射是否可以拿到枚举的实例对象呢?接下来我们进行一段尝试
尝试代码:
public enum Season {
spring("春季"),summer("夏季"),fall("秋季"),winter("冬季");
public String chineseName;
private Season(String chineseName){
this.chineseName=chineseName;
}
public static void reflectPrivateConstructor(){
Class c=null;
try {
// 获取 Class 对象
c=Class.forName("Season");
// 获取构造方法
Constructor<?> constructor=c.getDeclaredConstructor(String.class);
constructor.setAccessible(true);
// 调用 Constructor 的 newInstance 方法实例化
Season season=(Season) constructor.newInstance("春季");
System.out.println(season);
} catch (ClassNotFoundException | NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
reflectPrivateConstructor();
}
}
运行结果: 出现 java.lang.NoSuchMethodException
异常,意思就是没有这样的一个构造方法
原因分析:
当我们自定义一个枚举类时,它是会默认继承于 java.lang.Enum
的,因此 Enum 就相当于是我们自定义枚举类的父类,又因为子类实例化时要先帮父类进行构造,而上述代码中我们并没有帮父类进行构造。
Enum 源码分析:
首先分析 Enum 的定义我们发现它是一个抽象类,因此我们自定义的枚举类就会默认继承它
其次分析 Enum 的构造方法,我们发现它只有一个构造方法,且有着两个参数
帮助父类构造方式:
在枚举中,不是用 super 帮助父类进行构造,而是要将父类构造方法中的两个参数加入到反射时获取构造方法的 getDeclaredConstructor()
方法中,即上述代码中我们自定义的枚举类的构造方法有一个参数 chineseName,再加上父类构造方法中的 name 和 ordinal,故 getDeclaredConstructor()
方法的参数要有3个,注意参数的顺序,先放父类构造方法的参数,再放子类的
修改后的代码:
public enum Season {
spring("春季"),summer("夏季"),fall("秋季"),winter("冬季");
public String chineseName;
private Season(String chineseName){
this.chineseName=chineseName;
}
public static void reflectPrivateConstructor(){
Class c=null;
try {
// 获取 Class 对象
c=Class.forName("Season");
// 获取私有对象
Constructor<?> constructor=c.getDeclaredConstructor(String.class,int.class,String.class);
constructor.setAccessible(true);
Season season=(Season) constructor.newInstance("春季");
System.out.println(season);
} catch (ClassNotFoundException | NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
reflectPrivateConstructor();
}
}
运行结果: 出现 java.lang.IllegalArgumentException
异常,并且还说了 Cannot reflectively create enum objects,即不能通过反射创建枚举对象
原因分析:
通过报错我们可以知道是使用 Constructor 的 newInstance()
方法时出的错,因此我们来查看下它的源码
通过源码就一目了然了,源码上就已经杜绝通过反射创建枚举的对象了
优点:
缺点:
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/weixin_51367845/article/details/122020048
内容来源于网络,如有侵权,请联系作者删除!