设计模式:单例模式
一、单例模式介绍
1、定义
单例模式是Java中最简单的设计模式之一,此模式保证 某个类在运行期间,只有一个实例对外提供服务,而这个类被称为单例类。
2、使用单例模式要做的两件事
- 保证一个类只有一个实例
- 为该实例提供一个全局访问点
二、饿汉式
1、特点
在类加载期间初始化私有的静态实例,保证instance实例创建过程式线程安全的。
// 饿汉式
public class Singleton01 {// 私有化构造方法:禁止外部通过new的方式创建对象private Singleton01(){}// 通过static 在类加载期间创建私有化静态实例// 静态实例属于类,在类加载时会创建出来,并且只创建一次。这样可以保证线程安全,因为拿到的始终时同一个对象private static Singleton01 instance = new Singleton01();// 提供全局访问点public static Singleton01 getInstance(){return instance;}
}
二、懒汉式(线程不安全)
1、特点
支持懒加载(延迟加载),只有调用getInstance方法时,才会创建对象
2、问题
在多线程下,会有线程安全问题。
线程A 进入if判断后,还没new操作。线程B也通过if判断了。这样线程A、B 都会进行new操作。获取到两个不同的对象
// 懒汉式
public class Singleton02 {// 私有化构造方法:防止外部通过new的方式创建对象private Singleton02(){}// 不再这里创建对象了,调用getInstance方法时才创建private static Singleton02 instance;// 在调用getInstance方法中,判断没创建对象就新new一个对象返回public static Singleton02 getInstance(){if (instance == null){return new Singleton02();}return instance;}
}
三、懒汉式(线程安全)
1、特点
通过使用synchronized 锁,锁住单例模式对象的方法,防止多个线程同时调用问题。
2、缺点
对方法加了synchronized锁,并发就很低。
class Singleton03{private Singleton03(){}private static Singleton03 instance;//通过添加 synchronized 保证多线程模式下,单例模式对象的唯一性。public static synchronized Singleton03 getInstance(){if (instance == null){return new Singleton03();}return instance;}
}
四、懒汉式-双重校验
// 懒汉式 双重校验
public class Singleton04{private Singleton04(){}// 加volatile保证变量的可见性、屏蔽指令重排序private volatile static Singleton04 instance;// 通过添加 synchronized 保证多线程模式下,单例模式对象的唯一性。public static Singleton04 getInstance(){// 第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实例if (instance == null){// 第二次判断要等到抢到锁之后。synchronized (Singleton04.class) {if (instance == null) {instance = new Singleton04();/*** 上面的创建对象的代码,在JVM 中被分为三步:* 1、分配内存空间* 2、初始化对象* 3、将instance指向分配好的内存空间*/}}}return instance;}
}
五、静态内部类(懒加载)
静态内部类的特性:在静态内部类中创建单例,在转载内部类的时候,才会创建单例对象。
就是只有在调用Singleton4Static 的时候才会去new 对象。
/*** 单例模式- 静态内部类 (懒加载)* 根据静态内部类的特性,同时解决了 延时加载 程序安全的问题,并且代码更加简洁*/
class Singleton05{private Singleton05(){}// 创建静态内部类private static class Singleton4Static{private static Singleton05 instance = new Singleton05();}public static Singleton05 getInstance(){return Singleton4Static.instance;}
}
六、反射对单例对象的破坏
反射破坏的原理
突破封装:
setAccessible(true)
覆盖了Java的访问控制检查绕过初始化逻辑:直接调用构造方法,跳过了静态内部类的初始化控制
类加载机制失效:静态内部类方案依赖类加载保证单例,但反射在运行时创建新实例
class Reflect{public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {Class<Singleton05> clazz = Singleton05.class;Constructor<Singleton05> constructor = clazz.getDeclaredConstructor();// 设置为true后就可以对 类中的私有成员进行操作了。constructor.setAccessible(true);Singleton05 instance01 = constructor.newInstance();Singleton05 instance02 = constructor.newInstance();System.out.println(instance01 == instance02);}
}
七、防止反射破坏(以Singleton05代码举例)
在私有无参构造方法中加一个判断。
class Singleton05{private Singleton05(){if (Singleton4Static.instance != null){throw new RuntimeException("不允许非法访问");}}// 创建静态内部类private static class Singleton4Static{private static Singleton05 instance = new Singleton05();}public static Singleton05 getInstance(){return Singleton4Static.instance;}
}