内容由B站UP主产出,本文仅作为学习笔记
单例模式
定义:在程序运行过程中,只保持一个实例对象,就是使用了单例设计模式
优点:避免了创建对象、销毁对象的性能开销
懒汉式
public class LazySingleton{
private static LazySingleton lazySingleton = null;
private LazySingleton{}
public static synchronized LazySingleton getInstance(){
if(lazySingleton == null) {
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}
tips:
private
: 保证私有,外部无法new对象;static
: 必须是静态的,确保外部可直接调用getInstance()
方法synchronized
:确保线程安全,if(lazySingleton == null)
判断和lazySingleton = new LazySingleton();
之间可能因其他线程插入而导致对象重新初始化,造成新的对象产生破坏单例。
双重检查
public class DoubleCheckSingleton{
private static volatile DoubleCheckSingleton singleton = null;
private DoubleCheckSingleton{}
public static DoubleCheckSingleton getInstance(){
if (singleton == null) {
synchronized (DoubleCheckSingleton.class) {
if (singleton == null) {
singleton = new DoubleCheckSingleton();
}
}
}
return singleton;
}
}
tips:
volatile
: 需保证singleton在线程间的可见性
饿汉式
public class HungrySingleton{
// 方式一
// private static HungrySingleton singleton = new HungrySingleton();
// 方式二
private static HungrySingleton singleton = null;
static {
singleton = new HungrySingleton();
}
private HungrySingleton{}
public static HungrySingleton getInstance(){
return singleton;
}
}
原理与静态内部类相同
静态内部类
public class SingletonClass{
private static class InnerClass{
private static SingletonClass singleton = new SingletonClass();
}
private SingletonClass{}
public static SingletonClass getInstance(){
return InnerClass.singleton;
}
}
由于static修饰的类、方法、变量存在于方法区 / 元数据区中,所以其内部的SingletonClass singleton
成员变量也只会初始化一次,因此实现单例模式。
枚举
public enum EnumSingleton{
SINGLETON;
public static EnumSingleton getInstance(){
return SINGLETON;
}
}
tips:
枚举是实现单例的最佳方式,可以有效防止对单例模式的破坏
- 序列化和反序列化破坏单例模式
- 原因:反序列化创建对象时,会判断是否重写
readResolve
方法,如果重写了,则返回自己的单例对象;如果没有重写,则通过反射创建一个新的对象,反射会破坏单例模式。 - 解决办法:使用枚举 or 重写
readResolve
方法
private Object readResolve() { return singleton; }
- 原因:反序列化创建对象时,会判断是否重写
- 反射破坏单例模式
- 反射:通过字节码文件创建构造器对象,通过构造器初始化单例对象
- 注意,由于构造器是私有的,所以需要赋予权限
constructor.setAccessible(true);
- 注意,由于构造器是私有的,所以需要赋予权限
- 解决办法:使用枚举 or 阻止通过构造器创建对象(在构造器中手动抛出异常),例如
public class LazySingleton{ private static LazySingleton lazySingleton = null; private LazySingleton{ if(lazySingleton == null) { throw new RuntimeException("不允许通过反射创建单例对象"); } } public static synchronized LazySingleton getInstance(){ if(lazySingleton == null) { lazySingleton = new LazySingleton(); } return lazySingleton; } }
- 反射:通过字节码文件创建构造器对象,通过构造器初始化单例对象