单例设计模式
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
1 单例模式的结构
单例模式的主要有以下角色:
-
单例类。只能创建一个实例的类
-
访问类。使用单例类 【demo类】
2 单例模式的实现
【静态不能调用动态,jvm中类加载的时候 动态的成员变量和方法是在调用时才加载】一个加载时创建,一个调用时创建
单例设计模式分类两种:
饿汉式:类加载就会导致该单实例对象被创建
懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建
饿汉
【类加载而加载,所以没有线程安全问题】
【枚举方式】
-
饿汉式-方式1(静态变量方式)
// 问题1:为什么加 final----保证不被重写,破坏单例的方法 // 问题2:如果实现了序列化接口, 还要做什么来防止反序列化破坏单例---readResolve() 方法保证【名字定死了】会采用readResolve() 返回的对象 public final class Singleton implements Serializable { // 问题3:为什么设置为私有? 是否能防止反射创建新的实例?----还是有暴力反射,加if判断抛异常 private Singleton() {} // 问题4:这样初始化是否能保证单例对象创建时的线程安全?----静态成员变量在类加载器完成,JVM保证线程安全性 private static final Singleton INSTANCE = new Singleton(); // 问题5:为什么提供静态方法而不是直接将 INSTANCE 设置为 public, 说出你知道的理由----方法更好的封装性,比如懒惰的初始化。有更多的控制。提供泛型,变量的话就不可以泛型了 public static Singleton getInstance() { return INSTANCE; } //防止序列化破坏单例 public Object readResolve() { return INSTANCE; } }
说明:
该方式在成员位置声明Singleton类型的静态变量,并创建Singleton类的对象instance。instance对象是随着类的加载而创建的。如果该对象足够大的话,而一直没有使用就会造成内存的浪费。
-
饿汉式-方式2(静态代码块方式)
/** * 恶汉式 * 在静态代码块中创建该类对象 */ public class Singleton { //私有构造方法 private Singleton() {} //在成员位置创建该类的对象 private static Singleton instance; static { instance = new Singleton(); } //对外提供静态方法获取该对象 public static Singleton getInstance() { return instance; } }
说明:
该方式在成员位置声明Singleton类型的静态变量,而对象的创建是在静态代码块中,也是对着类的加载而创建。所以和饿汉式的方式1基本上一样,当然该方式也存在内存浪费问题。
-
饿汉式-方式2:枚举enum
// 问题1:枚举单例是如何限制实例个数的---enum几个就几个,相当于静态变量
// 问题2:枚举单例在创建时是否有并发问题---没有,类加载中完成
// 问题3:枚举单例能否被反射破坏单例----不能
// 问题4:枚举单例能否被反序列化破坏单例----不能,底层帮我们考虑了,enum实现了序列化器
// 问题5:枚举单例属于懒汉式还是饿汉式----恶汉
// 问题6:枚举单例如果希望加入一些单例创建时的初始化逻辑该如何做---枚举类中加构造方法
enum Singleton {
INSTANCE;
}
懒汉
-
懒汉式-方式1(线程不安全),因为线程会等待,误判为null
/** * 懒汉式 * 线程不安全 */ public class Singleton { //私有构造方法 private Singleton() {} //在成员位置创建该类的对象 private static Singleton instance; //对外提供静态方法获取该对象 public static Singleton getInstance() { if(instance == null) { //因为线程会等待,误判为null instance = new Singleton(); //不判断的话会new 第二个,虽然说类共享一个变量,地址变了 } return instance;