Java设计模式-3、单例模式
单例模式
单例模式属于创建型模式,⼀个单例类在任何情况下都只存在⼀个实例, 构造⽅法必须是私有的、由⾃⼰创建⼀个静态变量存储实例,对外提供⼀ 个静态公有⽅法获取实例。
优点是内存中只有⼀个实例,减少了开销,尤其是频繁创建和销毁实例的 情况下并且可以避免对资源的多重占⽤。
缺点是没有抽象层,难以扩展, 与单⼀职责原则冲突。
单例模式的设计规则
由定义我们可以很清晰的抽象出: 实现Java单例模式类有哪些通用设计规则?
(1)私有化类构造器。
(2)定义静态私有的类对象。
(3)提供公共静态的获取该私有类对象的方法。
了解了单例模式的概念,以及单例模式的通用设计规则,对于如何实现一个Java单例,应该是没什么阻碍了。这里我们还是要思考下单例模式的优点,或者说有啥好处,使用场景是什么?带着这些问题我们就能更好的设计单例模式。
为什么使用单例
1.Java单例模式解决了什么问题?
答:Java的单例模式主要解决了多线程并发访问共享资源的线程安全问题。
2.Java单例模式主要应用场景有哪些?
答:1.共享资源的访问与操作场景,如Windows系统的资源管理器,Windows系统的回收站,显卡的驱动程序,系统的配置文件,工厂本身(类模板),应用程序的日志对象等。
2.控制资源访问与操作的场景,如数据库的连接池,Java的线程池等。
单例模式的常⻅写法有哪些?
1. 饿汉式,线程安全

public class Singleton {private static final Singleton instance = new Singleton();// 私有的默认构造函数public Singleton() {}// 静态工厂方法public static Singleton getInstance() {return instance;}public void get() {System.out.println("get");}public static void main(String[] args) {Singleton.getInstance().get();}
}
public class Singleton {private static final Singleton instance = new Singleton();// 私有的默认构造函数private Singleton() {}// 静态工厂方法public static Singleton getInstance() {return instance;}public void get() {System.out.println("get");}// public static void main(String[] args) {
// Singleton.getInstance().get();
// }public static void main(String[] args) throws Exception {// 使⽤反射破坏单例// 获取空参构造⽅法Constructor<Singleton> declaredConstructor =Singleton.class.getDeclaredConstructor(null);// 设置强制访问declaredConstructor.setAccessible(true);// 创建实例Singleton singleton = declaredConstructor.newInstance();System.out.println("反射创建的实例" + singleton);System.out.println("正常创建的实例" +Singleton.getInstance());System.out.println("正常创建的实例" +Singleton.getInstance());}
}
结果:
2. 懒汉式,线程不安全
/
* 懒汉式单例,线程不安全
*
*/
public class Singleton {// 1、私有化构造⽅法private Singleton(){ }// 2、定义⼀个静态变量指向⾃⼰类型private static Singleton instance;// 3、对外提供⼀个公共的⽅法获取实例public static Singleton getInstance() {// 判断为 null 的时候再创建对象if (instance == null) {instance = new Singleton();}return instance;}
}
public class Test {public static void main(String[] args) {for (int i = 0; i < 3; i++) {new Thread(() -> {System.out.println("多线程创建的单例:" +
Singleton.getInstance());}).start();}}
}
结果:
懒汉式,线程安全
public class Singleton {// 1、私有化构造⽅法private Singleton(){ }// 2、定义⼀个静态变量指向⾃⼰类型private static Singleton instance;// 3、对外提供⼀个公共的⽅法获取实例public synchronized static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}
public class Singleton {// 1、私有化构造⽅法private Singleton() {}// 2、定义⼀个静态变量指向⾃⼰类型private volatile static Singleton instance;// 3、对外提供⼀个公共的⽅法获取实例public static Singleton getInstance() {// 第⼀重检查是否为 nullif (instance == null) {// 使⽤ synchronized 加锁synchronized (Singleton.class) {// 第⼆重检查是否为 nullif (instance == null) {// new 关键字创建对象不是原⼦操作instance = new Singleton();}}}return instance;}
}
这⾥为什么要使⽤ volatile ?
public class Singleton {// 1、私有化构造⽅法private Singleton() {}// 2、对外提供获取实例的公共⽅法public static Singleton getInstance() {return InnerClass.INSTANCE;}// 定义静态内部类private static class InnerClass{private final static Singleton INSTANCE = new
Singleton();}
}
枚举单例
public enum Singleton {INSTANCE;public void doSomething(String str) {System.out.println(str);}
}
Singleton singleton = Singleton.INSTANCE;
javap Singleton.class
Compiled from "Singleton.java"
public final class com.whm.demo.singleton.Singleton
extends
java.lang.Enum<com.whm.demo.singleton.Singleton> {public static final
com.spring.demo.singleton.Singleton INSTANCE;public static com.whm.demo.singleton.Singleton[]
values();public static com.whm.demo.singleton.Singleton
valueOf(java.lang.String);public void doSomething(java.lang.String);static {};
通过反射验证破坏枚举,实现代码如下:
public class Test {public static void main(String[] args) throws
Exception {Singleton singleton = Singleton.INSTANCE;singleton.doSomething("hello enum");// 尝试使⽤反射破坏单例// 枚举类没有空参构造⽅法,反编译后可以看到枚举有⼀个两个
参数的构造⽅法Constructor<Singleton> declaredConstructor =
Singleton.class.getDeclaredConstructor(String.class,
int.class);// 设置强制访问declaredConstructor.setAccessible(true);// 创建实例,这⾥会报错,因为⽆法通过反射创建枚举的实例Singleton enumSingleton =
declaredConstructor.newInstance();System.out.println(enumSingleton);}
}

查看反射创建实例的 newInstance() ⽅法,有如下判断:
所以⽆法通过反射创建枚举的实例。
单例模式总结:
Singleton 模式中的实例构造器可以设置为 protected 以允许子类派生。
Singleton 模式一般不要实现 Clone 接口,因为这有可能导致多个对象实例,与 Singleton 模式的初衷违背。
如何实现多线程环境下安全的 Singleton? 需注意对双检查锁的正确实现。