A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 达摩侠 于 2018-12-5 14:29 编辑

枚举作为一个常规的语言概念,一直到Java5才诞生不得不说有点奇怪,以至于到现在为止很多程序员仍然更喜欢用static final的形式去命名常量而不使用,一般情况下,Java程序员用这种方式去实现枚举:
[AppleScript] 纯文本查看 复制代码
class EnumByClass{
    public static final int RED=0;
    public static final int GREEN=1;
    public static final int BLUE=2;
}

这种方式实现的枚举也叫int枚举模式,尽管很常用,但是由int实现的枚举很难保证安全性,即当调用不在枚举范围内的数值时,需要额外的维护。另外 ,也不利于查看log和测试。
此时,我们需要开始使用Java的枚举类型,例如上面的int枚举模式类如果用enum实现,那么代码如下:
[AppleScript] 纯文本查看 复制代码
enum Color{
    RED,GREEN,BLUE;
}

上述是将枚举作为常量集合的简单用法,实际上,枚举更多的还是用于switch,也是在这时才能发现枚举相对于int枚举模式的好处,这里面举一个用enum实现switch的例子:
[AppleScript] 纯文本查看 复制代码
enum Color{
    RED,GREEN,BLUE;
}
public class Hello {
    public static void main(String[] args){
        Color color=Color.RED;
        int counter=10;
        while (counter-->0){
            switch (color){
                case RED:
                    System.out.println("Red");
                    color=Color.BLUE;
                    break;
                case BLUE:
                    System.out.println("Blue");
                    color=Color.GREEN;
                    break;
                case GREEN:
                    System.out.println("Green");
                    color=Color.RED;
                    break;
            }
        }
    }
}
如果我们用int枚举模式的话,诚然可以用一些类似++,——的语法糖,但是也要更多的考虑到安全性的问题。
如果仅此而已,那么枚举也没什么单独拿出来写的价值。
我个人对enum感兴趣主要是因为之前在写Singleton时有一个非常经验的枚举实现的单例,代码如下:
[AppleScript] 纯文本查看 复制代码
enum SingletonDemo{
    INSTANCE;
    public void otherMethods(){
        System.out.println("Something");
    }
}

简简单单的一点代码就实现了一个线程安全的单例,与其说是写法鬼斧神工,不如说是恰如其分地应用了enum的性质。
在用enum实现Singleton时我曾介绍过三个特性,自由序列化,线程安全,保证单例。这里我们就要探讨一下why的问题。
首先,我们都知道enum是由class实现的,换言之,enum可以实现很多class的内容,包括可以有member和member function,这也是我们可以用enum作为一个类来实现单例的基础。另外,由于enum是通过继承了Enum类实现的,enum结构不能够作为子类继承其他类,但是可以用来实现接口。此外,enum类也不能够被继承,在反编译中,我们会发现该类是final的。
其次,enum有且仅有private的构造器,防止外部的额外构造,这恰好和单例模式吻合,也为保证单例性做了一个铺垫。这里展开说下这个private构造器,如果我们不去手写构造器,则会有一个默认的空参构造器,我们也可以通过给枚举变量参量来实现类的初始化。这里举一个例子。
[AppleScript] 纯文本查看 复制代码
enum Color{
    RED(1),GREEN(2),BLUE(3);
    private int code;
    Color(int code){
        this.code=code;
    }
    public int getCode(){
        return code;
    }
}

需要注意的是,private修饰符对于构造器是可以省略的,但这不代表构造器的权限是默认权限。
目前我们对enum的结构和特性有了初步的了解,接下来探究一下原理层次的特性。
想要了解enum是如何工作的,就要对其进行反编译。
反编译后就会发现,使用枚举其实和使用静态类内部加载方法原理类似。枚举会被编译成如下形式:
public final class T extends Enum{
...
}
其中,Enum是Java提供给编译器的一个用于继承的类。枚举量的实现其实是public static final T 类型的未初始化变量,之后,会在静态代码中对枚举量进行初始化。所以,如果用枚举去实现一个单例,这样的加载时间其实有点类似于饿汉模式,并没有起到lazy-loading的作用。
对于序列化和反序列化,因为每一个枚举类型和枚举变量在JVM中都是唯一的,即Java在序列化和反序列化枚举时做了特殊的规定,枚举的writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法是被编译器禁用的,因此也不存在实现序列化接口后调用readObject会破坏单例的问题。
对于线程安全方面,类似于普通的饿汉模式,通过在第一次调用时的静态初始化创建的对象是线程安全的。
因此,选择枚举作为Singleton的实现方式,相对于其他方式尤其是类似的饿汉模式主要有以下优点:
1. 代码简单
2. 自由序列化
至于lazy-loading,考虑到一般情况不存在调用单例类又不需要实例化单例的情况,所以即便不能做到很好的lazy-loading,也并不是大问题。换言之,除了枚举这种方案,饿汉模式也在单例设计中广泛的被应用。例如,Hibernate默认的单例,获取sessionFactory用的HibernateUtil类建立方式如下:
[AppleScript] 纯文本查看 复制代码
public class HibernateUtil {
    private static final SessionFactory ourSessionFactory;

    static {
        try {
            Configuration configuration = new Configuration();
            configuration.configure();

            ourSessionFactory = configuration.buildSessionFactory();
        } catch (Throwable ex) {
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static Session getSession() throws HibernateException {
        return ourSessionFactory.openSession();
    }
}

这是一个典型的饿汉模式,考虑到这个单例只有一个方法即getSession,显然这种模式本身就是最优的且简洁的。这里面由于SessionFactory的创建并不是用系统默认的方式,如果想要用enum去实现反而麻烦且无必要。不过至少说明这样做也许需要一个解决自由序列化的问题。


点评

火火火,真的火了  发表于 2018-12-5 16:23

18 个回复

倒序浏览
沙发,写的特别好!!!
回复 使用道具 举报
炒鸡棒~~~
回复 使用道具 举报
赞一个
回复 使用道具 举报
HFFFX 来自手机 中级黑马 2018-12-5 14:48:24
报纸
虽然看不懂,但是一副很厉害的样子,哈哈哈
回复 使用道具 举报
回复 使用道具 举报
Vicky韦 来自手机 黑马粉丝团 2018-12-5 15:14:47
7#
厉害厉害
回复 使用道具 举报
写的太好了。
回复 使用道具 举报

太好了。
回复 使用道具 举报
老师优秀,我学到了
回复 使用道具 举报
回复 使用道具 举报
厉害了 ,赞一个,此贴必火
回复 使用道具 举报
好详细
回复 使用道具 举报
阿铁打 来自手机 初级黑马 2018-12-5 19:37:45
14#
666支持。。。。。。。。。
回复 使用道具 举报
怪蜀黍~ 来自手机 初级黑马 2018-12-5 21:52:57
15#
虽不明,但觉厉。虽然不懂但是就是觉得叼。
回复 使用道具 举报
虽然没学过,看不懂但是看着好牛逼的样子,顶起来,说不定以后用的着
回复 使用道具 举报
实用  我收下了
回复 使用道具 举报
优秀
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马