zoukankan      html  css  js  c++  java
  • Java枚举enum以及应用:枚举实现单例模式

    枚举作为一个常规的语言概念,一直到Java5才诞生不得不说有点奇怪,以至于到现在为止很多程序员仍然更喜欢用static final的形式去命名常量而不使用,一般情况下,Java程序员用这种方式去实现枚举:

    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实现,那么代码如下:

    enum Color{
        RED,GREEN,BLUE;
    }
    

    上述是将枚举作为常量集合的简单用法,实际上,枚举更多的还是用于switch,也是在这时才能发现枚举相对于int枚举模式的好处,这里面举一个用enum实现switch的例子:

    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时有一个非常经验的枚举实现的单例,代码如下:

    enum SingletonDemo{
        INSTANCE;
        public void otherMethods(){
            System.out.println("Something");
        }
    }
    

    简简单单的一点代码就实现了一个线程安全,lazy loading的单例,与其说是写法鬼斧神工,不如说是恰如其分地应用了enum的性质。

    在用enum实现Singleton时我曾介绍过三个特性,自由序列化,线程安全,保证单例。这里我们就要探讨一下why的问题。

    首先,我们都知道enum是由class实现的,换言之,enum可以实现很多class的内容,包括可以有member和member function,这也是我们可以用enum作为一个类来实现单例的基础。另外,由于enum是通过继承了Enum类实现的,enum结构不能够作为子类继承其他类,但是可以用来实现接口。此外,enum类也不能够被继承,在反编译中,我们会发现该类是final的。

    其次,enum有且仅有private的构造器,防止外部的额外构造,这恰好和单例模式吻合,也为保证单例性做了一个铺垫。这里展开说下这个private构造器,如果我们不去手写构造器,则会有一个默认的空参构造器,我们也可以通过给枚举变量参量来实现类的初始化。这里举一个例子。

    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类建立方式如下:

    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去实现反而麻烦且无必要。不过至少说明这样做也许需要一个解决自由序列化的问题。

  • 相关阅读:
    winForm ComboBox 控件默认值绑定及只可选择不可输入设定处理
    [c#]CacheHelper缓存类
    access数据库用sql语句添加字段,修改字段,删除字段
    35岁前程序员要规划好的四件事
    C#将网页内容转换成图片保存到本地( webbrowser 可应用于B/S结构中)
    SQL中返回刚插入记录的ID
    JIRA破解
    C#数组查找与排序
    最好的缺陷管理软件下载及破解Jira3.10 Enterprise Edition
    sql2000数据库 sql语句C#分页类代码
  • 原文地址:https://www.cnblogs.com/jmsjh/p/7379788.html
Copyright © 2011-2022 走看看