目录
单例模式
简介
定义
- 单例模式是创建型模式。
- 单例模式只涉及到一个单一的类,只负责创建自己的对象,同时确保只有单个对象被创建。(单例类必须自己创建自己的唯一实例。)
- 这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。(单例类必须给所有其他对象提供这一实例。)
- 有多种实现方式,懒汉式,饿汉式等等。
好处
- 单例模式能避免对资源的多重占用。
- 由于只创建了一个实例,减少了内存的开销。
缺点
- 没有接口,不能继承
- 与单一职责原则冲突
- 一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
实例
创建一个SIngleton类
创建一个SingleObject对象,确保只有单个对象被创建。
构造方法使用private使SingleObject类避免被实例化。
getInstance方法提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
//Singleton.java
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
public void showMessage(){
System.out.println("This is Singleton one.");
}
}
从 singleton 类获取唯一的对象
注意,这里由于之前创建对象时法使用private使SingleObject类避免被实例化,所以不能使用Singleton object = new Singleton();
,而应该使用getInstance方法
//SingletonPatternDemo.java
public class SingletonPatternDemo {
public static void main(String[] args) {
Singleton object =Singleton.getInstance();
object.showMessage();
}
}
运行结果
懒汉式
如果没有对象就创建,有就直接用
线程不安全,不支持多线程的懒汉式(未加锁)
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton(){}
public static LazySingleton getInstance(){
if(instance==null){
instance = new LazySingleton();
}
return instance;
}
}
线程安全,支持多线程的懒汉式
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton (){}
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
饿汉式
类加载时就初始化对象。
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。
public class HungrySingleton {
private static HungrySingleton instance = new HungrySingleton();
private HungrySingleton (){}
public static HungrySingleton getInstance() {
return instance;
}
}
懒汉式与饿汉式的根本区别在与是否在类内方法外创建自己的对象。 并且声明对象都需要私有化,构造方法都要私有化,这样外部才不能通过 new
对象的方式来访问。 饿汉式的话是声明并创建对象(因为他饿),懒汉式的话只是声明对象,在调用该类的 getinstance() 方法时才会进行
new 对象。
双重校验锁式double-checked locking
采用双锁机制,安全且在多线程情况下能保持高性能。
getInstance() 的性能对应用程序很关键。
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
登记式/静态内部类
对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
这种方式同样利用了 classloader 机制来保证初始化 instance 时只有一个线程
它跟第 3 种方式不同的是:第 3 种方式只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 lazy> loading 效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder> 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化> instance。
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
枚举
简洁,自动支持序列化机制,绝对防止多次实例化
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
总结
不推荐使用懒汉式,通常情况使用饿汉式,只有在要明确实现 lazy loading 效果时,才会使用登记式。如果涉及到反序列化创建对象时,可以尝试使用枚举方式。如果有其他特殊的需求,可以考虑使用双检锁方式。
关注博主,一起来学习设计模式吧