单例模式
描述
- 目标:保证某个对象 在整个软件系统中,只存在一个对象实例,并且只提供一个获取该对象的方法(静态方法)。
- 好处:节省内存空间,对于频繁创建销毁的对象,使用单例模式可以提高系统性能。
如:工具类,数据源,session工厂等。
主要分为两大类:饿汉式,懒汉式。
实现:
饿汉式:
// 饿汉式(静态变量)
class Singleton {
// 本类内部创建对象实例
private final static Singleton singleton = new Singleton();
// 私有化构造器
private Singleton(){
}
// 提供一个访问方法,返回实例对象
public static Singleton getInstance() {
return singleton;
}
}
// 饿汉式(静态代码块)
class Singleton2 {
// 本类内部创建对象实例
private static Singleton2 singleton2;
static { // 在静态代码块中创建
singleton2 = new Singleton2();
}
// 私有化构造器
private Singleton2(){
}
// 提供一个访问方法,返回实例对象
public static Singleton2 getInstance() {
return singleton2;
}
}
- 写法简单,避免线程同步问题、
类装载时就实例化,没有懒加载,可能会造成内存的浪费。
懒汉式:
// 线程不安全
class Singleton1 {
private static Singleton1 singleton1;
// 私有化构造器
private Singleton1() {
}
// 提供一个静态公用方法
public static Singleton1 getInstance() {
if (Objects.isNull(singleton1)) {
singleton1 = new Singleton1();
}
return singleton1;
}
}
// 线程安全
class Singleton2 {
// volatile 使更改立即更新到主存(可见性,有序性,该线程更改了,其它线程可以立马见到)
// JVM在实例化对象的时候,会进行优化和指令排序操作,使用双重检查,多线程情况下,不加该属性可能会出现空指针问题
private static volatile Singleton2 singleton2;
// 私有化构造器
private Singleton2() {
}
// 提供一个静态公用方法(双重检查,保证线程安全和效率)
public static Singleton2 getInstance() {
if (Objects.isNull(singleton2)) {
synchronized (Singleton2.class) {
if (Objects.isNull(singleton2)) {
singleton2 = new Singleton2();
}
}
}
return singleton2;
}
}
- 起到了懒加载的效果,应使用线程安全的方式。
静态内部类(懒汉式)
class SingletonOutClass {
// 私有化构造器
private SingletonOutClass() {
}
// 静态内部类,static 公用
private static class SingletonInnerClass {
private static final SingletonOutClass out = new SingletonOutClass();
}
// 获取静态内部类
public static SingletonOutClass getInstance() {
return SingletonInnerClass.out;
}
}
- JVM 外部类装载时,不会装载内部类,懒加载,
调用getInstance时才会一次类装载(类装载线程安全(静态属性,只会在第一次加载类的时候初始化)),线程安全,推荐
枚举(饿汉式)
enum SingletonEnum {
// 一个属性
SINGLETON_ENUM;
}
- 避免多线程同步,防止反序列化重新创建新的对象
JDK中的饿汉式单例:
Runtime.getRuntime();
破坏单例模式
可通过序列化或反射破坏单例模式(除了枚举,JVM底层实现的)
序列化,反序列化破坏单例模式
public class Test {
// 系列化,反序列化 破坏单例模式
public static void main(String[] args) throws IOException, ClassNotFoundException {
// writeObject();
Singleton singleton = readObject();
Singleton singleton2 = readObject();
Singleton singleton3 = Singleton.getInstance();
System.out.println(singleton == singleton2);
System.out.println(singleton == singleton3);
}
// 把对象写入文件(序列化)
public static void writeObject() throws IOException {
Singleton instance = Singleton.getInstance();
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("D:\\work\\study\\design\\single.txt"));
out.writeObject(instance);
out.close();
}
// 从文件读取数据(反序列化)
public static Singleton readObject() throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D:\\work\\study\\design\\single.txt"));
Singleton singleton = (Singleton) objectInputStream.readObject();
objectInputStream.close();
return singleton;
}
}
class Singleton implements Serializable {
private static final Singleton singleton = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return singleton;
}
}
反射破坏单例模式
public class Test2 {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 获取字节码对象
Class<Singleton2> singleton2Class = Singleton2.class;
// 获取无参构造方法对象
Constructor<Singleton2> declaredConstructor = singleton2Class.getDeclaredConstructor();
// 取消反问检查
declaredConstructor.setAccessible(true);
// 创建Singleton2对象
Singleton2 singleton1 = declaredConstructor.newInstance();
Singleton2 singleton2 = declaredConstructor.newInstance();
System.out.println(singleton1 == singleton2);
}
}
class Singleton2 {
private Singleton2() {
}
public static class Singleton22 {
private static final Singleton2 INSTANCE = new Singleton2();
}
public static Singleton2 getInstance() {
return Singleton22.INSTANCE;
}
}
解决反序列化破坏单例模式的方式:
重写readResolve()方法
(原理:当调用反序列化时,会自动调用该方法,将该方法的返回值直接返回)
class Singleton implements Serializable {
private static final Singleton singleton = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return singleton;
}
// 当进行反序列化时,会自动调用该方法,将该方法的返回值直接返回
public Object readResolve() {
return singleton;
}
}
解决反射破坏单例模式
(在创建对象的地方判断)
class Singleton2 {
private static boolean flag = false;
// 解决反射破坏单例模式
private Singleton2() {
synchronized (Singleton2.class) {
if (flag) {
throw new RuntimeException("已存在对象,不可创建");
}
flag = true;
}
}
public static class Singleton22 {
private static final Singleton2 INSTANCE = new Singleton2();
}
public static Singleton2 getInstance() {
return Singleton22.INSTANCE;
}
}