单例模式深入
admin
2024-03-20 06:51:46

前言:今天公司让我搭建设计模式的课程,结果在单例模式被教训了一波,发现有很多都没有深入

反射如何破坏单例模式

1.单例类:

package 单例模式;/*** @author diao 2022/3/10*/
public class Singleton {private Singleton(){}private static Singleton singleton=null;//静态工厂方法public static Singleton getInstance(){if(singleton==null){singleton=new Singleton();}return singleton;}
}

2.反射破坏单例模式:
本质利用类的构造方法得到类的实例

package 单例模式;/*** @author diao 2022/3/10*/
public class Singleton {private Singleton(){}private static Singleton singleton=null;//静态工厂方法public static Singleton getInstance(){if(singleton==null){singleton=new Singleton();}return singleton;}
}

结果:
671631440 935563443
发现是不同的实例——>原因:反射获得单例类的构造函数,使得单例类失效

3.如何预防?
因为我们反射它是调用了零次构造函数所以我们只需要防止连续被调用两次构造函数即可,需要进行次数统计(多线程下需要保证次数count不安全的情况)
在构造函数里面进行上锁判断count如果>0说明前面有人来过,抛出异常即可,否则count++;

public class Singleton {private static int count = 0;private static Singleton instance = null;private Singleton(){synchronized (Singleton.class) {if(count > 0){throw new RuntimeException("创建了两个实例");}count++;}}public static Singleton getInstance() {if(instance == null) {instance = new Singleton();}return instance;}public static void main(String[] args) throws Exception {Constructor constructor = Singleton.class.getDeclaredConstructor();constructor.setAccessible(true);Singleton s1 = constructor.newInstance();Singleton s2 = constructor.newInstance();}}

结果抛出异常

反序列化破坏单例模式

1.单例类
这里采用的是懒汉式获取我们的实例,并且定义count判断来防止反射破坏单例问题,并且懒汉的get采用了双重检测防止多线程造成并发导致创建多个不同的实例

/*** 双重检测锁的单例模式*/
class DoubleIfSynchronizedSingleton implements Serializable {private static final long serialVersionUID = 3288137447362611668L;private static int count = 0;  //创建对象次数private volatile static DoubleIfSynchronizedSingleton singleton = null;private DoubleIfSynchronizedSingleton() {//解决反射破坏单例问题synchronized (DoubleIfSynchronizedSingleton.class) {if (count > 0){throw new RuntimeException("你居然敢破坏我的单例.....");}count++;}}/*** 单例对象生成方法* @return*/public static DoubleIfSynchronizedSingleton getSingleton() {if (singleton == null) {synchronized (DoubleIfSynchronizedSingleton.class) {if (singleton == null) {singleton = new DoubleIfSynchronizedSingleton();}}}return singleton;}
}

2.反序列化破坏单例模式


/*** 反序列化破坏单例模式*/
public class DeserializedBreakSingleton {@Testpublic void test(){//通过单例模式获取对象DoubleIfSynchronizedSingleton singleton = DoubleIfSynchronizedSingleton.getSingleton();//通过反序列化获取对象DoubleIfSynchronizedSingleton deserializedSingleton = DeserializedBreakSingleton.getSerializedSingleton(singleton);System.out.println("hashCode of singleton:"+singleton);System.out.println("hashCode of deserializedSingleton:"+deserializedSingleton);System.out.println(singleton == deserializedSingleton);}/*** 通过序列化获取单例对象*/private static DoubleIfSynchronizedSingleton getSerializedSingleton(DoubleIfSynchronizedSingleton singleton){File file = new File("singleton");ObjectOutputStream oos = null;ObjectInputStream ois = null;DoubleIfSynchronizedSingleton newSingleton = null;try {oos = new ObjectOutputStream(new FileOutputStream(file));oos.writeObject(singleton);oos.flush();ois = new ObjectInputStream(new FileInputStream(file));newSingleton = (DoubleIfSynchronizedSingleton) ois.readObject();} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}finally {if (ois != null){try {ois.close();} catch (IOException e) {e.printStackTrace();}}if (oos != null){try {oos.close();} catch (IOException e) {e.printStackTrace();}}if(file.exists()){file.delete();}}return newSingleton;}
}

结果:
hashCode of singleton:serialization.DoubleIfSynchronizedSingleton@1376c05c hashCode of deserializedSingleton:serialization.DoubleIfSynchronizedSingleton@e2d56bf false

3.解决
我们在单例类中加上readResolve()方法即可
原因: readObject()方法回通过一个三元运算来判断创建序列化的对象,如果能实例化就创建一个新的对象,但是单例类肯定不是能够实例化的,但是readObject()方法中还会判断是否有readResolve,如果有的话就根据这个方法返回对象,所以反序列化回来的对象是一个新的对象而不是之前单例那个对象;

/*** 双重检测锁的单例模式*/
class DoubleIfSynchronizedSingleton implements Serializable {private static final long serialVersionUID = 3288137447362611668L;private static int count = 0;  //创建对象次数private volatile static DoubleIfSynchronizedSingleton singleton = null;private DoubleIfSynchronizedSingleton() {//解决反射破坏单例问题synchronized (DoubleIfSynchronizedSingleton.class) {if (count > 0){throw new RuntimeException("你居然敢破坏我的单例.....");}count++;}}/*** 单例对象生成方法* @return*/public static DoubleIfSynchronizedSingleton getSingleton() {if (singleton == null) {synchronized (DoubleIfSynchronizedSingleton.class) {if (singleton == null) {singleton = new DoubleIfSynchronizedSingleton();}}}return singleton;}/*** 解决反序列化破坏单例问题*/private Object readResolve(){return singleton;}
}

枚举实现单例类

1.单例类:
在实现过程中,Java虚拟机会保证枚举类型不能被反射并且构造函数只被执行一次。

public class Singleton {private Singleton(){}   public static enum SingletonEnum {SINGLETON;private Singleton instance = null;private SingletonEnum(){instance = new Singleton();}public Singleton getInstance(){return instance;}}
}

2.测试类:
单例模式的枚举实现代码比较简单,而且又可以利用枚举的特性来解决线程安全和单一实例的问题,还可以防止反射和反序列化对单例的破坏,因此在很多书和文章中都强烈推荐将该方法作为单例模式的最佳实现方法

    ……	public static void main(String args[]) {Singleton s1 = SingletonEnum.SINGLETON.getInstance();Singleton s2 = SingletonEnum.SINGLETON.getInstance();System.out.println(s1==s2);}……

相关内容

热门资讯

摸鱼、钓虾、吃瓜、赏荷…初夏时... 这个周末,一场场充满野趣的“田园嘉年华”在沪郊金山多个农场上演,吸引众多市民带着孩子下乡来,赛跑、吃...
原创 戚... 5月28日,北京环球影城迎来了一对温暖的家庭画面:戚薇和李承铉携三岁半的儿子Seven现身游玩。现场...
滹沱河畔 遇见“诗和远方” 图为市民在滹沱河畔休闲娱乐。 初夏五月,惠风和畅。徜徉在石家庄滹沱河生态区(城区段),澄澈河水蜿蜒...
在迪士尼排队两小时,我才看清V... 文丨沈理 在网上看到一则新闻: 上海迪士尼,创极速光轮排队区。一个父亲牵着七八岁的儿子,已经在烈日...
重庆文旅喊你去吃火锅、观山水、... 本网讯(草原云·正北方网记者 马丽侠)火锅、机车、文创、演艺……5月28日下午,重庆市文化和旅游发展...