1、==和equals方法
枚举相等比较中,==和equals方法没有任何区别,查看源码你会发现,equals方法的实现就是通过==判断的。
2、compareTo方法
Enum类的compareTo方法比较的其实是枚举的顺序大小,所以先定义的枚举项要“小于”后定义的枚举项。
3、枚举是实现单例的最好的方式
采用双重校验锁实现单例模式
public class Singleton {
private volatile static Singleton singleton;
private Singleton(){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
“双重锁校验”方式虽然保证了线程安全,但是无法解决反序列化会破坏单例模式的问题。
采用枚举实现单例
public enum Singleton {
INSTANCE;
public void whateverMethon() {}
}
枚举可以解决线程安全问题,因此和上面的双重校验锁方式比代码简洁明了。这是因为枚举在反编译后,属性都带有static。static类型的属性会在类加载之后被初始化,当第一个Java类第一次被真正使用时静态资源被初始化,Java类的加载和初始化过程都是线程安全的,因为虚拟机在加载枚举的类时,会使用ClassLoader的loadClass方法,而这个方法使用同步代码保证了线程安全。所以创建一个enum类型时线程安全的。
那为什么说枚举可以解决反序列化会破坏单例模式问题呢?
普通的Java类的反序列化过程中,会通过反射调用类的默认构造函数来初始化对象。所以,即使单例中的构造函数是私有的,也会被反射破坏。由于反序列化后的对象是重新new出来的,所以就破坏了单例模式。而枚举的反序列化并不是通过反射实现的,是通过enmu类中的valueOf方法来根据名字查找对象,所以也就不会发生由于反序列化导致的单例破坏问题了。(每一个枚举项在JVM中都是单例的,因为序列化+反序列化可以破坏单例,所以Java就针对枚举的序列化作出了特殊规定)。
4、接口的返回值中不能使用枚举类型
有A和B两个服务,A提供一个接口给B服务,接口的方法中返回有有个枚举Atype,刚开始这个枚举汇总有2个值enum1和enum2。但是某一天A升级了,返回的枚举中多了enum3,B未升级,在反序列化的过程中使用valueOf方法在枚举类中找enum3,找不到就会抛出IllegalArgumentException异常。
值得注意的是,在代码开发中,可以使用javadoc的@see注解表明这个字符串的取值从哪个枚举中获取。