1. 概念
枚举是在JDK1.5之后引入的,主要用途是:将一组常量组织起来,在这之前表示一组常量通常使用定义常量的方式:
public static final int RED = 1;
public static final int GREEN = 2;
public static final int BLACK = 3;
但是常量举例有不好的地方,例如:碰巧有个数字1,编译器可能误解为RED,现在我们可以直接用枚举来进行组织,这样一来,就拥有了类型,而不是普通的整型1
public enum TestEnum {RED,BLACK,GREEN;
}
优点:将常量组织起来统一管理
场景:错误状态码,消息类型,颜色的划分,状态机等等。。。
本质:是 java.lang.Enum 的子类,也就是说,自己写的枚举类型,就算没有显式继承Enum,但其默认继承了这个类
2. 使用
2.1 switch语句
示例:
public enum TestEnum {//枚举对象RED,BLACK,GREEN,WHITE;public static void main(String[] args) {TestEnum testEnum = RED;switch(testEnum) {case BLACK:System.out.println("BLACK");break;case RED:System.out.println("RED");break;case GREEN:System.out.println("GREEN");break;default:System.out.println("颜色错误");break;}}
}
2.2 常用方法
方法名称 | 描述 |
values() | 以数组形式返回枚举类型的所有成员 |
ordinal() | 获取枚举成员的索引位置 |
valueOf() | 将普通字符串转换为枚举实例 |
compareTo() | 比较两个枚举成员在定义时的顺序 |
示例:
public enum TestEnum {//枚举对象RED,BLACK,GREEN,WHITE;public static void main(String[] args) {//values() 以数组形式返回枚举类型的所有成员TestEnum[] testEnums = TestEnum.values();for (int i = 0; i < testEnums.length; i++) {System.out.println(testEnums[i]);}System.out.println("============");//ordinal() 获取枚举成员的索引位置for (int i = 0; i < testEnums.length; i++) {System.out.println(testEnums[i].ordinal());}System.out.println("============");//valueOf() 将普通字符串转换为枚举实例TestEnum val = TestEnum.valueOf("RED");//TestEnum val = TestEnum.valueOf("RED2");//error,该字符串必须在枚举对象中有对应的System.out.println(val);System.out.println("============");//compareTo() 比较两个枚举成员在定义时的顺序//因为枚举默认继承于Enum,Enum实现了Comparable接口,所以可以compareToSystem.out.println(BLACK.compareTo(GREEN));}
}
运行结果:
查看Enum源码可以发现,该类只有一个构造方法,且是私有的,所以我们可以如下定义使用枚举:
public enum TestEnum {//枚举对象RED(1,"红色"),BLACK(2,"黑色"),GREEN(3,"绿色"),WHITE(4,"白色");public String color;public int ordinal;private TestEnum(int ordinal,String color) {this.color = color;this.ordinal = ordinal;}
}
3. 枚举优点缺点
优点:
- 枚举常量更简单安全
- 枚举有内置方法,代码更优雅
缺点:
- 不可继承,无法扩展
4. 枚举与反射
枚举通过反射来拿实例化对象可行否?
在反射中,任何一个类,哪怕其构造方法是私有的,我们也可以通过反射拿到它的实例对象,下面用反射来拿枚举的实例对象:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;public class Test {public static void main(String[] args) {Class<?> c1 = null;try {c1 = Class.forName("enumdemo.TestEnum");//这里传参不仅要传给自己重写的构造方法,还需要传参给父类的构造方法Constructor<?> constructor = c1.getDeclaredConstructor(String.class,int.class,int.class,String.class);constructor.setAccessible(true);TestEnum testEnum = (TestEnum)constructor.newInstance("haha",10,19,"白色");System.out.println(testEnum);} 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);}}
}
运行结果:
我们查看报错的源码可以发现
枚举在这里被过滤了,我们不能通过反射来获取枚举类的实例
该现象有关于面试题:为什么枚举实现单例模式是安全的
5. 面试问题
5.1 写一个单例模式
public class Singleton {private volatile static Singleton uniqueInstance;private Singleton() {}public static Singleton getInstance() {if (uniqueInstance == null) {synchronized (Singleton.class){if(uniqueInstance == null){//进入区域后,再检查一次,如果仍是null,才创建实例uniqueInstance = new Singleton();}}}return uniqueInstance;}}
5.2 用静态内部类实现一个单例模式
class Singleton {/** 私有化构造器 */private Singleton() {}/** 对外提供公共的访问方法 */public static Singleton getInstance() {return UserSingletonHolder.INSTANCE;}/** 写一个静态内部类,里面实例化外部类 */private static class UserSingletonHolder {private static final Singleton INSTANCE = new Singleton();}}public class Main {public static void main(String[] args) {Singleton u1 = Singleton.getInstance();Singleton u2 = Singleton.getInstance();System.out.println("两个实例是否相同:"+ (u1==u2));}}
5.3 用枚举实现一个单例模式
public enum TestEnum {INSTANCE;public TestEnum getInstance(){return INSTANCE;}public static void main(String[] args) {TestEnum singleton1=TestEnum.INSTANCE;TestEnum singleton2=TestEnum.INSTANCE;System.out.println("两个实例是否相同:"+(singleton1==singleton2));}}