Enum源码分析

来源:互联网 发布:鬼吹灯 盗墓笔记 知乎 编辑:程序博客网 时间:2024/06/05 23:41

在JDK1.5之前,Java有两种方式定义新类型: 类和接口。 对于大部分面向对象编程来说,这两种方法看起来似乎足够了。但是当我们需要限定某个类型只能出现特点的值的时候,我们就需要做很多工作来完成这种限定,而且还有可能以为设计问题而导致无法完美的作出限制或限制过度是某些应有的功能无法完成。为此,sun公司在jdk1.5是增了Enum来解决这个问题。

下面我们看一下Enum的源码:


接下来我们对个人认为比较重要的部分代码进行分析,若有错漏的地方欢迎指正,在此谢过!

public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable { 

从Enum的声明代码中我们可以看到,Enum是一个实现了Comparable和Serializable接口的抽象类。所以Enum其实是一个类,它拥有着所以抽象类的特性和方法,同时实现了Comparable和Serializable接口的方法。

而为了保证每一个Enum类型及其定义的枚举变量在JVM中都是唯一的,Enum使用了以下3个方法来确保其不会被复制。

1、作为一个抽象方法,意味着我们无法将其进行实例化,而这个功能使其能够有效防止通过反射的反射对其进行复制操作,是保护其在系统中唯一性的一个重要手段。在进行复制操作的获取构造函数时会抛出如下异常:


2、在java中每一个Object都能够通过实现clone方法来对对象进行复制,为了避免用户通过这个方法来对其进行复制,Enum自己重写了clone方法来覆盖Object的方法。

protected final Object clone() throws CloneNotSupportedException {<span style="white-space:pre"></span>throw new CloneNotSupportedException();}
由上面的代码我们可以看到当我们调用对象的克隆方法时程序直接抛出CloneNotSupportedExeception异常,所以Enum是不支持克隆的。

3、在java中要复制一个对象还有一种方法是使用序列化和反序列化的方法对对象进行复制,对实现了Serializable的类,我们可以先将其对象序列化然后在通过readObject(ObjectStream in)读取回来进行反序列化得到一个跟原来相同的对象。为此Enum通过实现Serializable接口并重写readObject方法的方式对这种行为进行有效的阻止。

/**     * prevent default deserialization     */    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");    }
这样,Enum就禁止了Serializable接口的readObject方法,使用户无法对对象进行复制。
因为我了解Enum的初衷是更好的理解java单例模式中的枚举方法,所以这里也主要讲解了Enum如何保证唯一性的一些措施,其他的源码注释的挺细致的,有兴趣的可以自己了解一下。

-----------------------------------------------------------------------------华丽分割线------------------------------------------------------------------------------------------------------

说了那么久的Enun类,但是我们在java中却是通过enum关键字来声明枚举类的,这跟这里所讲的Enum类有什么关系呢?

首先我们来声明一个enum:

public enum t {    SPRING,SUMMER,AUTUMN,WINTER;}
and then 反编译一下:

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        });    }}
从反编译得到的代码我们可以看到当我们使用enum申明一个枚举时,编译器自动为我们生成了一个继承自Enum的类,同时使用final进行修饰,以阻止其它类的继承。而枚举T中的枚举项至是该枚举类的(public static  final类型的)对象,各对象按在类中定义的顺序从0开始依次赋予ordinal值做为其编号,并存储到VALUES数组中。

通过这些内容我们就可以和轻易的了解我们平时使用枚举的过程中各种行为的原理了。希望这篇博文对大家有用,如有错漏欢迎指正!

本文后半部分借鉴了一篇文章的内容,有如果对后面的内容还有不理解的可以看看能否有所帮助。

参考:深入分析java的枚举类型


0 0
原创粉丝点击