Java设计模式(三):单例模式

作者 : 开心源码 本文共2984个字,预计阅读时间需要8分钟 发布时间: 2022-05-14 共157人阅读

一、单例模式

在任何开发语言中,单例模式应该算是大家基乎最先接触和学习的设计模式,由于,它最为简单也最为常用。

单例模式的特点:

  • 有且仅有一个实例;
  • 构造方法为私有;
  • 其实例只能由自己来创立;

单例模式的使用场景:

  • 全局配置;
  • client端客户缓存;
  • 等等;

单例也适时使用,不要觉得简单就乱用!

二、Java中的实现方式

大家能够用多少种方式来实现单例模式?
至少我能用5种方式来实现,假如还有更多其它方式,也欢迎在评论区留言。

2.1、简单暴力

/** *  没有推迟加载,但却最简单 */public class Singleton {        private static final Singleton instance = new Singleton();                private Singleton(){}        public static Singleton getInstance(){            return instance;    }}

2.2、推迟加载

针对 2.1 在类加载时就初始化,这里采用了推迟初始化。

/** *     推迟加载 */public class Singleton {    private static Singleton instance = null;    private Singleton(){}    public synchronized static Singleton getInstance(){        if(instance == null){            instance = new Singleton();        }        return instance;    }}

2.3、静态内部类来推迟加载

/** *     静态内部类,加载时没有初始化 instance,因而达到了推迟加载 */public class Singleton {    private static class InternalSingleton{        private static final Singleton instance = new Singleton();    }    private Singleton(){}    public static Singleton getInstance(){        return InternalSingleton.instance;    }}

2.4、双检索(DCL)

这种方式,大家要注意,网上有些是错误的。

public class Singleton {    private static volatile Singleton instance = null; // 这里需要 volatile    private Singleton(){}    public static Singleton getInstance(){        if(instance == null){            synchronized(Singleton.class){                if(instance == null){                    instance = new Singleton();                }            }        }        return instance;    }}

为何要加上 volatile ,大家可以去看我的《小白系列五:关键字volatile》。

2.5、枚举方式(JDK 1.5才支持)

public enum Singleton{    instance;  // 默认 instance = this    public void function(){        // ......    }}

三、五种方式的比照

3.1、绝对单实例

5种方式都能正确的实现单例,但只有『枚举方式』才是最为安全的,因而,它不支持反射,而其它方式尽管私有化了默认构造函数,但是,我们能可以通过反射的方式来产生多实例。

import java.lang.reflect.Constructor;public class Demo {    public static void main(String[] args) {        Singleton singleton1 = Singleton.getInstance();        Singleton singleton2 = null;        try {            Constructor constructor = Singleton.class.getDeclaredConstructor();            constructor.setAccessible(true);            singleton2 = (Singleton) constructor.newInstance();        } catch (Exception e) {            e.printStackTrace();        }        System.out.println("singleton1 = " + singleton1);        if (singleton2 != null) {            System.out.println("singleton2 = " + singleton2);        }    }}

打印结果如下:

singleton1 = Singleton@610455d6singleton2 = Singleton@511d50c0

我们可以看到,Singleton 变成了多实例。

而枚举则不允许反射,JDK 源码中有给出判断:

// java.lang.reflect.Constructor.javapublic final class Constructor<T> extends Executable {    .....        @CallerSensitive    public T newInstance(Object ... initargs)        throws InstantiationException, IllegalAccessException,               IllegalArgumentException, InvocationTargetException    {        if (!override) {            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {                Class<?> caller = Reflection.getCallerClass();                checkAccess(caller, clazz, null, modifiers);            }        }                // 这里会判断类的修饰符,假如是枚举则报错        if ((clazz.getModifiers() & Modifier.ENUM) != 0)            throw new IllegalArgumentException("Cannot reflectively create enum objects");        ConstructorAccessor ca = constructorAccessor;   // read volatile        if (ca == null) {            ca = acquireConstructorAccessor();        }        @SuppressWarnings("unchecked")        T inst = (T) ca.newInstance(initargs);        return inst;    }        ......}

3.2、内存消耗

相比较静态常量,枚举会略微多消耗点内存,由于枚举首先还是一个类,而后实例化几个由 final 修饰这个类的对象,每个实例都带有自己的少量元信息。而常量没有这一层封装,只占用基本的内存(包括引用和它的值本身),因而,要简单轻巧;假如值是基本类型而不是包装类型,那占用的内存就更少了。

枚举的使用,和单例的使用一样,我们也要适度,不能滥用;同样,大家在使用枚举时也不要担心这多出来的一点点内存开销,该用的时候还是要用。同时,我们需要注意一点:枚举的出现,能够帮助我们提升代码可读性,以及更好的可扩展性;因而,相比较多出来的一点点内存开销,不建议以牺牲代码可读性、可维护性、开发效率等而不使用枚举。

总之,Java的单例模式,我更推荐使用枚举来实现!

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » Java设计模式(三):单例模式

发表回复