java 枚举的特点
- java中定义的枚举类型,经过编译器的处理最终会变成一个对象的定义。此对象继承Enum类
- 枚举变量其实就是类的静态变量。
java 中的Enum类
Enum类内部有一个构造函数,该构造函数只能由编译器调用,是无法手动操作的。
Enum类是一个抽象类。
public abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable {
/**
* 定义的枚举变量的名,大多数程序员使用toString()返回name值
*/
private final String name;
public final String name() {
return name;
}
/**
* 定义枚举变量在枚举中声明的顺序,下表从0开始。主要适用于一下两个数据结构。
* {@link java.util.EnumSet} and {@link java.util.EnumMap}.
*/
private final int ordinal;
public final int ordinal() {
return ordinal;
}
/**
* 枚举的构造方法,程序员不能直接调用,是通过编译器调用的
* @param name - 枚举名称
* @param ordinal - 枚举定义的顺序,默认从0开始
*/
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
/**
*toString方法返回枚举的name值,此方法可以被覆盖;
*/
public String toString() {
return name;
}
/**
*枚举值比较大小,是通过创建枚举时的序数比较的。
*/
public final int compareTo(E o) {
Enum<?> other = (Enum<?>)o;
Enum<E> self = this;
if (self.getClass() != other.getClass() &&
self.getDeclaringClass() != other.getDeclaringClass())
throw new ClassCastException();
return self.ordinal - other.ordinal;
}
/**
* 获取枚举对象
/*
public static <T extends Enum<T>> T valueOf(Class<T> enumType,String name) {
//enumType.enumConstantDirectory()获取到的是一个map集合,key值就是name值,value则是枚举常量值。
//enumConstantDirectory是class对象内部的方法,更加class对象获取一个map集合的值。
T result = enumType.enumConstantDirectory().get(name);
if (result != null)
return result;
if (name == null)
throw new NullPointerException("Name is null");
throw new IllegalArgumentException(
"No enum constant " + enumType.getCanonicalName() + "." + name);
}
/**
* Throws CloneNotSupportedException.
* 此方法为了保证单例模式,阻止被克隆
* @return (never returns)
*/
protected final Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
/**
* 防止enum类反序列化
*/
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
throw new InvalidObjectException("can't deserialize enum");
}
private void readObjectNoData() throws ObjectStreamException {
throw new InvalidObjectException("can't deserialize enum");
}
...
举例:
创建一下枚举:
举例一:
public enum TestEnum {
instance;
}
通过javap 命令进行反编译
public final class TestEnum extends java.lang.Enum<TestEnum> {
public static final TestEnum instance;
private static final TestEnum[] ENUM$VALUES;
static {};
private TestEnum(java.lang.String, int);
public static TestEnum[] values();
public static TestEnum valueOf(java.lang.String);
}
举例二:
public final class TestEnum2 extends java.lang.Enum<TestEnum2> {
public static final TestEnum2 test1;
public static final TestEnum2 test2;
private final int a;
private static final TestEnum2[] ENUM$VALUES;
static {};
private TestEnum2(java.lang.String, int, int);
public int a();
public static TestEnum2[] values();
public static TestEnum2 valueOf(java.lang.String);
}
反编译编译器为我们生成的类:
1.静态的方法values()方法
public static TestEnum[] values()
{
return (TestEnum[]) $VALUES.clone();
}
2.编译器自动添加类静态的valueOf()方法
public static TestEnum valueOf(String s)
{
return (TestEnum)Enum.valueOf(enumType,s);
}
3.私有构造函数
private TestEnum(String s,int i)
{
super(s,i)
}
4.定义的枚举实例,如:
public static final TestEnum instance;
5.在static块中实例化枚举
static
{
instance = new TestEnum("instance",0);
$VALUES = (new TestEnum[]{instance});
}
6.枚举与class对象
当枚举实例向上转型为Enum类型后,values()方法失效,即无法一次性获取所有枚举实例变量,但是由于Class对象的存在,可以获取:
Enum testEnum = TestEnum.instance;
Class<?> clazz = e.getDeclaringClass();
if(clazz.isEnum())
{
TestEnum[] tes = (TestEnum[])class.getEnumConstants();
}
枚举进阶
- 向enum类添加方法与自定义构造函数(构造函数私有)
- enum类不能继承
- 覆盖enum类方法。(只能覆盖toString()方法,因为别的是final类型)。
- 枚举中定义抽象方法(然后使每个枚举实例都实现该方法)
public enum EnumDemo3
{
FIRST{
@Override
public String getInfo()
{
return "FIRST TIME";
}
}
public abstract String getInfo();
}
常见单例模式的问题
单例的序列化可能会破坏单例模式。
1.在每次反序列化时,都会创建一个新的实例。
解决方案如下:
在反序列化石,直接返回当前INSTANCE,使用readResolve()方法。
public class Singleton implements java.io.Serializable
{
public static Singleton INSTANCE = ...;
private Singleton(){
}
private Object readResolve()
{
return INSTANCE;
}
}
- 使用反射强行调用私有构造器,解决方式修改构造器,让它在创建第二个实例的时候抛出异常。
private static volatile boolean flag = true;
private Singleton()
{
if (flag)
{
flag = false;
}
else
{
throw new RuntimeException("");
}
}
注:枚举单例可以解决以上所有的问题。
枚举的序列化
枚举序列化是由JVM保证的,每一个枚举类型和定义的枚举变量在JVM中都是唯一定义的。
序列化:Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过java.lang.Enum.valueOf()方法根据名字查找枚举对象。同时,编译器是不允许任何对这种序列化机制的定制并禁用writeObject,readObject,readResolve等方法。
通过反射获取构造器并创建枚举是不可能的。
SingletonEnum singleton = constructor.newInstance("0",q);
因为newInstance的源码中规定:
if(clazz.getModifiers()&&Modifier.ENUM)
{
throw new IllegalArgumentException("");
}
单例总结
1.线程安全 2.序列化和反序列安全
3.延迟加载 4.反射安全