一、什么是设计模式
设计模式就是积累的经验,老一辈的it工作者经过长时间的累计,总结出来好的解决方案。
二、单例设计模式
2-1 什么是单例设计模式
就是一个类只能创建一个对象。
2-2 单例模式 ---- 饿汉式
饿汉式就是在类加载完之后会直接创建一个实例。
package com.su.alg.design;
import java.io.Serializable;
/**
* @Author swj
* @Date 2023/3/28 14:57
* @Description: 单例模式 -- 懒汉式
* @Version 1.0
*/
public class Singleton1 implements Serializable {
//1、私有化构造方法
private Singleton1() {
if(SINGLETON_1 != null) {
throw new RuntimeException("单例对象已创建,不能再创建对象");
}
System.out.println("Singleton init...");
}
//2、创建一个常量,new一个当前对象。
private static final Singleton1 SINGLETON_1 = new Singleton1();
//3、提供一个方法让外面获取这个对象
public static Singleton1 getSingleton() {
return SINGLETON_1;
}
public static void otherMethod() {
System.out.println("Other Method ...");
}
public Object readResolve() {
return SINGLETON_1;
}
}
2-3 破坏饿汉式单例模式的三种方式以及解决方案
使用放射创建对象
Class<Singleton1> singleton1Class = Singleton1.class;
try {
Constructor<?>[] declaredConstructors = singleton1Class.getDeclaredConstructors();
Constructor<?> declaredConstructor = declaredConstructors[0];
declaredConstructor.setAccessible(true);
Object o = declaredConstructor.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
解决方式:
在构造方法中判断对象是否创建,如果创建了就报错。
使用反序列化创建对象
//反序列化
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(singleton);
ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
Object o = objectInputStream.readObject();
System.out.println(o == singleton);
解决方式:
在对象中写一个readResolve方法返回唯一一个实例。
通过jdk的unsafe创建对象
Object object = UnsafeUtils.getUnsafe().allocateInstance(Singleton1.class);
解决方案:无
综合测试代码:
package com.su.alg.design;
import org.springframework.objenesis.instantiator.util.UnsafeUtils;
import java.io.*;
import java.lang.reflect.Constructor;
/**
* @Author swj
* @Date 2023/3/28 15:05
* @Description: TODO
* @Version 1.0
*/
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException {
Singleton1.otherMethod();
System.out.println("--------------------------");
Singleton1 singleton = Singleton1.getSingleton();
Singleton1 singleton2 = Singleton1.getSingleton();
System.out.println(singleton == singleton2);
Class<Singleton1> singleton1Class = Singleton1.class;
try {
Constructor<?>[] declaredConstructors = singleton1Class.getDeclaredConstructors();
Constructor<?> declaredConstructor = declaredConstructors[0];
declaredConstructor.setAccessible(true);
Object o = declaredConstructor.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
//反序列化
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(singleton);
ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
Object o = objectInputStream.readObject();
System.out.println(o == singleton);
Object object = UnsafeUtils.getUnsafe().allocateInstance(Singleton1.class);
System.out.println(singleton == object);
}
}
运行结果:
2-4 单例模式 --- 枚举饿汉式
实现代码:与饿汉式类似
/**
* @Author swj
* @Date 2023/3/28 17:13
* @Description: 单例模式 -- 枚举饿汉式
* @Version 1.0
*/
public enum Singleton2 {
INSTANCE;
Singleton2() {
System.out.println("singleton2 init...");
}
public static Singleton2 getInstance() {
return INSTANCE;
}
public static void otherMethod() {
System.out.println("other method ...");
}
}
反射、反序列化无法破坏枚举饿汉式的单例模式。
2-5 单例模式 --- 懒汉式
当调用获取对象的方法才会创建对象,运行在多线程,需要考虑线程安全问题。
实现代码:
package com.su.alg.design;
/**
* @Author swj
* @Date 2023/3/28 17:13
* @Description: 单例模式 -- 懒汉式
* @Version 1.0
*/
public class Singleton3 {
private static Singleton3 singleton3 = null;
public static synchronized Singleton3 getInstance() {
if(null == singleton3) {
singleton3 = new Singleton3();
}
return singleton3;
}
public static void otherMethod() {
System.out.println("other method ...");
}
}
2-6 单例模式 --- 懒汉式DCL
就是在创建对象的同时考虑多线程的问题
package com.su.alg.design;
/**
* @Author swj
* @Date 2023/3/28 17:13
* @Description: 单例模式 -- 懒汉式DCL
* @Version 1.0
*/
public class Singleton4 {
/**
* volatile 保证可见性有序性*
*/
private static volatile Singleton4 singleton4 = null;
/**
* 如果第一次需要创建对象才去做线程安全,接下去就不用再做线程锁,需要双检索*
* @return
*/
public static Singleton4 getInstance() {
if(null == singleton4) {
synchronized (Singleton4.class) {
if(null == singleton4) {
singleton4 = new Singleton4();
}
}
}
return singleton4;
}
public static void otherMethod() {
System.out.println("other method ...");
}
}
为什么要使用vaolatile?
由于cpu在执行代码的时候存在指令重排序,所以可能Singleton还未创建完毕,其他线程已经拿到空的对象。
volatile的作用是将变量赋值的指令排序到最后面,从而将指令重排序造成的影响解决。
2-7 单例模式 --- 内部类懒汉式
/**
* @Author swj
* @Date 2023/3/28 17:13
* @Description: 单例模式 -- 内部类模式
* @Version 1.0
*/
public class Singleton5 {
private Singleton5() {
System.out.println("singleton5 init...");
}
private static class Holder {
static Singleton5 singleton5 = new Singleton5();
}
public static Singleton5 getInstance() {
return Holder.singleton5;
}
public static void otherMethod() {
System.out.println("other method ...");
}
}
2-8 jdk中使用到的单例模式
Runtime
System 中的console对象
Collections