Java修炼之路——基础篇——枚举

枚举的用法
每个枚举变量其实都是枚举类的一个实例。

枚举与单例
各种模式的单例模式,包括枚举实现的单例

//懒汉模式
class SingletonA {
	private static SingletonA instance = new SingletonA();
	//保证不能通过new SingletonB的方式创建对象
	private SingletonA(){}
	public static SingletonA getInstance() {
		return instance;
	}
}

//保证效率的懒加载
class SingletonB {
	private static SingletonB instance;
	private SingletonB(){}
	public static SingletonB getInstance() {
		if(instance == null) {
			synchronized (SingletonB.class) {
				if (instance == null) {
					instance = new SingletonB();
				}
			}
		}
		return instance;
	}
}

//内部类懒汉模式
class SingletonC {
	private SingletonC(){}
	
	private static class SingletonHolder {
		private static final SingletonC instance = new SingletonC();
	}
	
	public SingletonC getInstance() {
		return SingletonHolder.instance;
	}
}

//内部枚举类懒加载模式
class SingletonD {
	private SingletonD(){}
	
	private enum EnumSingleton {
		instance;
		//因为枚举变量是static final的,所以如果不是定义时声明,那只能在构造方法中实例化,并且有且只能实例化一次。
		//所以保证了resource对象的单例性。
		private SingletonD singletonD;
		private EnumSingleton() {
			singletonD = new SingletonD();
		}
	}
	
	public SingletonD getInstance() {
		return EnumSingleton.instance.singletonD;
	}
}

枚举如何比较
可以用equals(),也可以用==,推荐使用==
enum的equals()方法实现如下:

public final boolean equals(Object other) {
        return this==other;
    }

可以看出,也是调用的==实现的。
官方文档说明如下:

JLS 8.9 Enums 一个枚举类型除了定义的那些枚举常量外没有其他实例了。 试图明确地说明一种枚举类型是会导致编译期异常。
在枚举中final clone方法确保枚举常量从不会被克隆,而且序列化机制会确保从不会因为反序列化而创造复制的实例。
枚举类型的反射实例化也是被禁止的。
总之,以上内容确保了除了定义的枚举常量之外,没有枚举类型实例。

另外,==和equals()的不同如下:

== 不会抛出 NullPointerException
enum Color { BLACK, WHITE };

Color nothing = null;
if (nothing == Color.BLACK);      // runs fine
if (nothing.equals(Color.BLACK)); // throws NullPointerException

== 在编译期检测类型兼容性
enum Color { BLACK, WHITE };
enum Chiral { LEFT, RIGHT };

if (Color.BLACK.equals(Chiral.LEFT)); // compiles fine
if (Color.BLACK == Chiral.LEFT);      // DOESN'T COMPILE!!! Incompatible types!

总而言之,在枚举比较上使用 == , 因为: 1. 能正常工作 2. 更快 3. 编译期是安全的 4. 运行时也是安全的

switch 对枚举的支持
枚举 switchcase 标签必须为枚举常量的非限定名称
用法如下:

//定义枚举
public enum Color {
	RED,
	GREEN,
	YELLOW;
}

//使用枚举
public void compare(Color color) {
		switch (color) {
		case RED:
			break;
		case YELLOW:
			break;	
		default:
			break;
		}
	}

枚举的线程安全性问题
枚举的序列化如何实现
枚举是线程安全的,描述如下:

1:枚举的实现方式

//定义枚举
public enum t {
    SPRING,SUMMER,AUTUMN,WINTER;
}
//反编译枚举类
public final class T extends Enum
{
    private T(String s, int i)
    {
        super(s, i);
    }
    public static T[] values()
    {
        T at[];
        int i;
        T at1[];
        System.arraycopy(at = ENUM$VALUES, 0, at1 = new T[i = at.length], 0, i);
        return at1;
    }
 
    public static T valueOf(String s)
    {
        return (T)Enum.valueOf(demo/T, s);
    }
 
    public static final T SPRING;
    public static final T SUMMER;
    public static final T AUTUMN;
    public static final T WINTER;
    private static final T ENUM$VALUES[];
    static
    {
        SPRING = new T("SPRING", 0);
        SUMMER = new T("SUMMER", 1);
        AUTUMN = new T("AUTUMN", 2);
        WINTER = new T("WINTER", 3);
        ENUM$VALUES = (new T[] {
            SPRING, SUMMER, AUTUMN, WINTER
        });
    }
}

通过反编译后代码我们可以看到,public final class T extends Enum,说明,该类是继承了Enum类的,同时final关键字告诉我们,这个类也是不能被继承的。
并且方法与属性都是static的,而static类型的属性会在类被加载之后被初始化。当一个Java类第一次被真正使用到的时候静态资源被初始化、Java类的加载和初始化过程都是线程安全的。所以,创建一个enum类型是线程安全的。

2:枚举的序列化
JavaDoc中的描述:

扫描二维码关注公众号,回复: 5495709 查看本文章
序列化的时候Java仅仅是将枚举对象的name属性输出到结果中,
反序列化的时候则是通过java.lang.Enum的valueOf方法来根据名字查找枚举对象。
同时,编译器是不允许任何对这种序列化机制的定制的,
因此禁用了writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法。 

我们看一下这个valueOf方法:

public static <T extends Enum<T>>T valueOf(Class<T> enumType,String name) {  
            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 const " + enumType +"." + name);  
        }

从代码中可以看到,代码会尝试从调用enumType这个Class对象的enumConstantDirectory()方法返回的map中获取名字为name的枚举对象,如果不存在就会抛出异常。再进一步跟到enumConstantDirectory()方法,就会发现到最后会以反射的方式调用enumType这个类型的values()静态方法,也就是上面我们看到的编译器为我们创建的那个方法,然后用返回结果填充enumType这个Class对象中的enumConstantDirectory属性。

所以,JVM对序列化有保证。
此段描述来自于:http://blog.jobbole.com/94074/

猜你喜欢

转载自blog.csdn.net/zhaohong_bo/article/details/88052814
今日推荐