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

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

反射如何破坏单例模式

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天4晚大... 暑假带孩子去贵州旅游5天4晚大概多少钱,贵州5天4夜的旅游价格,避坑指南。最近我终于下定决心,在暑假...
【仙境宽甸】逃离热浪!宽甸消夏... 希望这个夏天●西瓜甜一点 当全国进入“烧烤模式”,辽东的宽甸却像被遗忘的天然空调房!这里森林覆盖率7...
避坑指南:奶茶店照明设计中易被... 要知道对于奶茶店而言,照明设计对于店铺流量与销量的提升还是有着很大作用的,所以做好照明设计对于顾客体...
如何高效利用早晨阅读练习题目?... 早晨阅读练习题目是有效利用时间来增强学习成效的途径,然而,众多人并不清楚如何进行高效的操作。以下,我...
驴友川藏线偶遇一徒步00后大爷... 驴友川藏线偶遇一徒步00后大爷 :分手后想不开 出来徒步散散心 四川dou知道 西藏 00后
万源市文化体育和旅游局 户外露... 露营户外安全提示 广大游客朋友:  为了保证您和他人的安全,请选择具有正规资质的露营地露营,遵守好露...
【东非】:六驱猛兽+变形金刚+... 营地英文:Roving Bushtops 营地位置:坦桑尼亚,塞伦盖蒂 【东非顶级移动行宫】 ...
In-N-Out真的要凉了?“... 真是风水轮流转啊… In-N-Out的口碑和热度也是急转直下。 USA TODAY 最新发布的 “...
石岛渔港千帆竞发,镜头下的渔家... 荣成,山东半岛最东端的宝藏小城,三面被大海拥抱,坐拥壮丽的“天尽头”海景打卡点。这里既可以遇见嶙峋的...
陈麻花:重庆磁器口排队王,酥脆... 在重庆磁器口古镇,陈麻花无疑是最亮眼的美食名片之一,被誉为 “排队王”。它凭借酥脆的口感、独特的麻辣...
原创 推... “从手到口,从口到心,中国人延续着对世界和人生特有的感知方式。只要点起炉火,端起碗筷,每个平凡的人,...
原创 超... “从手到口,从口到心,中国人延续着对世界和人生特有的感知方式。只要点起炉火,端起碗筷,每个平凡的人,...
青岛市即墨区:联动多方力量,让... 近日,即墨区潮海街道联动多方力量开展“畅游青岛 文明相伴”主题活动,从机关表率到社区治理,从文明交通...
重磅发布丨庆祝西藏自治区成立6... 在西藏自治区成立60周年之际,西藏自治区党委宣传部、西藏广播电视台共同出品的大庆主题概念片《心约西藏...
游客投喂屡禁不止!记者探访上海... 上海动物园一岁半的棕熊"军军"被大家称为"西郊达菲",自出生以来深受游客喜爱。他原本活泼好动,但近期...
厦门旅游3天2晚最详细路线安排... 厦门旅游3天2晚最详细路线安排,南普陀寺玩三天估计消费多少 大家好呀,我是你们的导游小陈!厦门这座...
积极备战,蓄力全运!广东男篮结... 随着昨日广东男篮79-69战胜西班牙职业男篮明星队,近两周三站七场的热身赛征程告一段落。通过与各国劲...