Java设计模式

Java 单例实现解析

2019-09-22  本文已影响0人  大哥你先走

什么时候使用Singleton

Singleton指仅仅被实例化一次的类。Singleton通常用来代表那些本质上唯一的系统组件,比如文件系统,窗口管理器,日历等。Singleton的类会使客户端测试变得异常困难,因为无法给Singleton替换模拟实现,除非Singleton实现一个充当其类型的接口。


Java类的实例化

按照是否调用类的构造器,可以简单的将类实例化的方法分为两大类:通过构造器实例化和不通过构造器实例化。下面以实例化 Windows10FileSystem 类为例进行详细说明。

Windows10FileSystem.java

public class Windows10FileSystem {
    private String name;
    private String description;
    // use default constructor
    // getter/setter method
}

这个文件系统十分的“简陋”,只包括文件系统名称和文件系统描述。

通过构造器实例化类

不使用构造器实例化类

Singleton的实现

实现Singleton的思路

上一节我们已经了解Java中实例化一个类的多种方法,而Singleton的目标就是要确保类仅仅只被实例化一次,为此我们需要控制类实例化的入口,或者控制入口方法的调用次数或者控制方法每次调用返回同一个对象,确保一个类只被实例化一次。

实现Singleton的三种方法

有多种方法可以实现Singleton,虽然每种方法的具体细节不一样,但是每种方法的目标都是相同的:确保类只被实例化一次。强烈推荐下文描述的第一种方法来实现Singleton:使用单元素枚举类型实现Singleton。

单元素枚举类型实现Singleton

Java从1.5发型版本开始支持通过枚举(enum)实现Singleton,下面以enum 实现一个Windows 10文件系统,这个文件系统非常的简陋,只提供了名字和描述两项基本信息,具体的代码如下:

public enum Windows10FileSystem {
    /**
     * singleton file system instance
     */
    INSTANCE("Windows 10", "graphical operating system");

    private String name;
    private String description;

    Windows10FileSystem(String name, String separator) {
        this.name = name;
        this.description = separator;
    }

    public String getBaseInfo() {
        return name + "\t" + description;
    }
}

使用enum 实现Singleton更加简洁,无偿提供了序列化机制,绝对防止多次实例化,即使是在面对复杂的序列化和反射攻击时,这种方法依然绝对可靠。强烈推荐使用该方法实现Singleton。

导出公有静态成员(final 域)实现Singleton
public class Windows10FileSystem implements Serializable {
    private static final AtomicBoolean FIRST_INSTANTIATION = new AtomicBoolean(true);
    public static final Windows10FileSystem INSTANCE = new Windows10FileSystem("Windows 10","graphical operating system");

    transient private String name;
    transient private String description;

    /**
     * 构造器私有化,并防止多次调用
     * @param name 文件系统名称
     * @param separator 文件系统描述
     */
    private Windows10FileSystem(String name, String separator) {
        if (FIRST_INSTANTIATION.get()) {
            FIRST_INSTANTIATION.compareAndSet(true,false);
            this.name = name;
            this.description = separator;
        } else {
            throw new UnsupportedOperationException("windows file system can only be instantiated once");
        }
    }

    /**
     * 防止反系列化攻击
     * @return file system object
     */
    private Object readResolve() {
        return INSTANCE;
    }

    public String getBaseInfo() {
        return name + "\t" + description;
    }
}

static变量的初始化顺序参考JLS 8.7

公有静态工厂方法实现Singleton

这种方法相比与导出公有静态成员(final 域)实现Singleton而言只是公有静态变量变成了一个工厂方法,每次调用工厂方法都返回同一个实例。

private static final Windows10FileSystem INSTANCE = new Windows10FileSystem("Windows 10","graphical operating system");
public static Windows10FileSystem getInstance() {
        return INSTANCE;
    }

两种实现Singleton方法的核心都是通过私有化构造器来控制类的实例化。公有域方法的主要优势在于,类的成员声明很清楚的表明这个类是一个Singleton(可读性强):公有的静态域是final的,所以该域将总是包含相同的对象。公有域在性能上已经不再拥有任何优势,现代化的JVM实现几乎都能将静态工厂方法的调用内联化。静态工厂方法的优势在于,它提供了更高的灵活性:在不改变API的条件下,我们可以改变该类是否是Singleton的想法。工厂方法返回该类的唯一实例,但是,这可以很容易的被修改,比如修改为每一个线程返回同一个实例。

上一篇下一篇

猜你喜欢

热点阅读