单例模式的7种写法

一、概述

在介绍单例模式的写法之前,我们有必要了解一下什么是单例模式。 单例模式是一种常用的软件设计模式,在他的核心结构中只包含一个被称为 单例的特殊类。通过单例模式可以保证系统只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
单例模式结构图
单例模式结构图
作用:保证一个类仅有一个实例,并提供一个访问它的全局访问点
适用场景:1. 应用中某个实例对象需要频繁的被访问。
2. 应用中每次启动只会存在一个实例。如账号系统,数据库系统。

二、代码

下面我们来看看单例模式的各种写法以及简单的介绍:
1.饿汉式

public class Singleton {
        private static Singleton singleton = new Singleton();
        public static Singleton getInstance(){
            return singleton;
        }
}

这种方式在类加载时就完成了对象的初始化,所以类加载比较慢,但是获取对象的速度比较快。这种方式可以避免多线程的同步问题,但是我们并不确定这个对象是否需要加载,所以没有达到懒加载的效果。
2.懒汉式(线程不安全)

public class Singleton {
        private static Singleton singleton ;
        public static Singleton getInstance(){
            if(singleton == null){
                singleton = new Singleton();
            }
            return singleton;
        }
}

这种方式采用了懒加载模式,在第一次使用时才去初始化对象,节省了资源,但是第一次使用时需要初始化,反映稍微慢一些,同时会产生线程安全的问题。
3.懒汉式(线程安全)

public class Singleton {
        private static Singleton singleton ;
        public static Singleton getInstance(){
            synchronized (Singleton.class) {
            if(singleton == null){
                    singleton = new Singleton();
                }
            }
            return singleton;
        }
}

或者

public class Singleton {
        private static Singleton singleton ;
        public synchronized static Singleton getInstance(){
            if(singleton == null){
                    singleton = new Singleton();
                }
            return singleton;
        }
}

这种方式可以在多线程环境下安全工作,但是每次调用getInstance()方法都要进行同步,造成不必要的开开销,而且大部分时候我们是用不到同步的,所以不建议采用这种方式。
4.双重检查式(DCL:Double-checked locking)

public class Singleton {
    private static Singleton singleton;
    public static Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

这种写法在getInstance()方法中进行了两次空判断,第一次是为了避免不必要的同步,第二次是在singleton为空的情况下才创建实例。DCL虽然在一定程度上解决了资源的消耗和多余的同步,线程安全等问题,但是在某些情况下会出现DCL失效。在某些书中建议使用静态内部类单例模式来代替DCL.
5.静态内部类单例模式

public class Singleton {
    public static Singleton getInstance() {
        return SingletonHolder.singleton;
    }
    private static class SingletonHolder {
        private static final Singleton singleton = new Singleton();
    }
}

首先需要明确一个结论:加载一个类时,其内部的类不会同时被加载,当且仅当某个静态成员(静态成员变量,构造方法,静态方法)被调用时才去加载。第一次加载Singleton时并不会初始化singleton,只有第一次掉用那个getInstance()方法时才会加载SingletonHolder,并且初始化singleton,这样不仅能够保证线程的安全性也能保证Singleton类的唯一性,所以推荐使用这种方式。
6.枚举单例

public enum Singleton{
        INSTANCE;
        public void doSomething(){

        }
}

你可以通过Singleton.INSTANCE来访问,比较方便,线程安全,防止反序列化创建新的对象,但是失去了一些类的特性,没有延迟加载,而且可读性较差,所以很少有人使用。
7.容器式

public class SingletonManager {
        public static Map<String, Object> objMap = new HashMap<String, Object>();
        public static void registerService(String key,Object instance){
            if(!objMap.containsKey(key)){
                objMap.put(key, instance);
            }
        }
        public static Object getService(String key){
            return objMap.get(key);
        }
}

用SingletonManager将多个单例统一进行管理,使用时根据key获取对应的实例,这种方式可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,屏蔽了内部的实现细节,降低了耦合度。

单例模式代码

public enum SingleTon2 {
A{//每个实例的实例方法
public int getType(){
return 1;
}
},B,C;
private static int value = 0; //类方法
public static int getValue(){
return value;
}
public int type = 2;//实例方法
public int getType(){
return type;
}
}

在每个实例中,我们可以覆盖getType方法,这个getType方法必须提供,否则外部无法调用。

外部调用

int value = SingleTon2.getValue();
int type = SingleTon2.A.getType();//结果是1

如果枚举类中有抽象方法,则要求每个实例需要实现这个方法
这里写图片描述

枚举实现单例

public class SingleTon2 {

public enum SingleTonHolder{//枚举的构造函数是私有的
    INSTANCE;//枚举的实例是static final的
    private SingleTon2 instance;
    SingleTonHolder(){
        instance = new SingleTon2();
    }
    public SingleTon2 getInstance(){
        return instance;
    }
}

}

外部调用

SingleTon2 instance = SingleTon2.SingleTonHolder.INSTANCE.getInstance();

同时,枚举也实现了Serializable接口,可以在网络中传输

给出一篇文章参考单例模式各版本的原理与实践

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值