设计模式-单例模式
概念
确保一个类只有一个实例,并提供一个全局访问点来访问这个唯一实例
场景
- 配置类
- 需要频繁实例化然后销毁的对象
- 创建对象时耗时过多或者耗资源过多,但又经常用到的对象
- 有状态的工具类对象:计数或者生成序列信息
- 频繁访问数据库或文件的对象
实现
要素
- 私有构造方法 (禁止其他程序创建该类的对象)。
- 私有静态引用指向自己实例 (此对象供外部程序使用,要保证唯一,通常定义为instance)。
- 以自己实例为返回值的公有静态方法 (通常定义为getInstance()方法)。
1.恶汉模式
public class SingleTon {
//私有静态域,用来存储单利对象
private static SingleTon instance = new SingleTon();
//私有的构造方法,防止其他对象新建
private SingleTon() {}
//共有的静态访问接口,用来获取单例
public static SingleTon getInstance() {
return instance;
}
多线程下是不安全的,而且不是延迟初始化
2.懒汉模式
2.1 单线程模式
public class SingleTon {
//私有静态域,用来存储单例对象
private static SingleTon instance ;
//私有的构造方法,防止其他对象新建
private SingleTon() {}
//共有的静态访问接口,用来获取单利
public static SingleTon getInstance() {
if(Objects.isNull(instance)) {
instance = new SingleTon();
}
return instance;
}
}
2.2 多线程模式
对于单线程不安全的2.1可以在获取对象的时候加锁判断。但是不是每次获取都需要加锁,我们只需要在instance没有实例化,才需要加锁,所以使用双重检测
import java.util.Objects;
public class SingleTon {
//私有静态域,用来存储单例对象
private static SingleTon instance ;
//私有的构造方法,防止其他对象新建
private SingleTon() {}
//共有的静态访问接口,用来获取单例
public static SingleTon getInstance() {
if(Objects.isNull(instance)) {
synchronized(instance){
if(Objects.isNull(instance)) {
instance = new SingleTon();
}
}
instance = new SingleTon();
}
return instance;
}
}
2.2这种是双重检测的实现方式,但是这种方式会遭到反射的破坏
import java.lang.reflect.Constructor;
public class SingleMain {
public static void main(String[] args) throws Exception{
SingleTon origin1 = SingleTon.getInstance();
SingleTon origin2 = SingleTon.getInstance();
SingleTon origin3 = null;
Constructorconstructor = SingleTon.class.getDeclaredConstructor();
constructor.setAccessible(true);
origin3 = constructor.newInstance();
System.out.println(origin1==origin2);
System.out.println(origin1==origin3);
}
}
2.3 防止反射破坏
import java.util.Objects;
public class SingleTon {
//私有静态域,用来存储单利对象
private static SingleTon instance ;
private static boolean flag = false;
//私有的构造方法,防止其他对象新建
private SingleTon() {
synchronized(SingleTon.class) {
if(flag==false) {
System.out.println("创建成功");
flag = true;
}else {
throw new RuntimeException("单例遭受攻击");
}
}
}
//共有的静态访问接口,用来获取单利
public static SingleTon getInstance() {
if(Objects.isNull(instance)) {
synchronized(SingleTon.class){
if(Objects.isNull(instance)) {
instance = new SingleTon();
}
}
}
return instance;
}
}
如果实体可以序列化的话,则可能被序列化破坏
首先单例实现序列化接口
public class SingleMain {
public static void main(String[] args) throws Exception{
SingleTon origin1 = SingleTon.getInstance();
SingleTon origin2 = SingleTon.getInstance();
System.out.println(origin1==origin2);
SingleTon origin3 = null;
// Constructorconstructor = SingleTon.class.getDeclaredConstructor();
// constructor.setAccessible(true);
// origin3 = constructor.newInstance();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("ser.io")));
oos.writeObject(origin1);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("ser.io")));
origin3 = (SingleTon)ois.readObject();
System.out.println(origin1==origin3);
}
}
由于序列化的时候调用的是readResolve所以这个可以通过实现这个方法避免
import java.io.Serializable;
import java.util.Objects;public class SingleTon implements Serializable{
private static final long serialVersionUID = 1L;
//私有静态域,用来存储单利对象
private static SingleTon instance ;
private static boolean flag = false;
//私有的构造方法,防止其他对象新建
private SingleTon() {
synchronized(SingleTon.class) {
if(flag==false) {
System.out.println("创建成功");
flag = true;
}else {
throw new RuntimeException("单例遭受攻击");
}
}
}
//共有的静态访问接口,用来获取单利
public static SingleTon getInstance() {
if(Objects.isNull(instance)) {
synchronized(SingleTon.class){
if(Objects.isNull(instance)) {
instance = new SingleTon();
}
}
}
return instance;
}
private Object readResolve(){
return instance;
}
}
除此之外还有使用枚举和静态内部类实现单例模式
双重检测会有一个失效的问题,这个和JVM加载机制有关系可以通过生命instance属性为volatile避免