设计模式-创建型模式-单例设计模式
创始人
2024-11-16 17:37:16
0

        创建型模式提供创建对象的机制,能够提升已有代码的灵活性和复用性;

        常用的有:单例模式、工厂模式、建造设模式;不常用的:原型模式;

1.概述

        单例模式是最简单的模式之一,其保证了某个类在运行期间只有一个实例对外提供服务;

1.1 满足单例模式的条件

  • 保证一个类只有一个实例;
  • 为该实例提供一个全局访问点;

2.单例模式的实现方式

        单例模式有两种实现方式:饿汉式与懒汉式

2.1 饿汉式

        在类加载期间初始化私有的静态实例,保证实例在创建过程是线程安全的;

        不支持懒加载,获取速度较快,但是对象大且不常用的话会造成内存的浪费;

/**  * @author : luobei  * @date : 2024/7/31 14:36  * 单例模式-饿汉式  */ public class SingletonHungry {      // 1.私有构造方法     private SingletonHungry(){}          // 2.在本类中创建私有静态的全局对象     private static SingletonHungry instance = new SingletonHungry();          // 3.提供一个全局访问点     public static SingletonHungry getInstance(){         return instance;     } }

2.2 懒汉式

        支持懒加载,只有第一次调用getInstance方法时才会创建对象,只有在getInstance方法上添加sychronized锁才能保证线程安全,但加了锁后并发度会比较低,因此不适合常用的类;

/**  * @author : luobei  * @date : 2024/7/31 14:49  * 单例模式-懒汉式(线程不安全)  */ public class SingletonLazy {     // 1.私有构造方法     private SingletonLazy(){}      // 2.在本类中创建私有静态的全局对象     private static SingletonLazy instance;      // 3.提供一个全局访问点     public static SingletonLazy getInstance(){         //通过判断对象是否被初始化,来选择是否创建对象         if(instance == null){             instance = new SingletonLazy();         }         return instance;     } }

        或者可以使用模块锁,二次判断来保证创建完成之后的getInstance调用的并发度,而且本类中的静态变量需要添加volatile修饰,保证变量可见性,避免指令重排;

        指令重排是指 jvm 会根据性能自动重新排序创建对象时的顺序,所以有的时候过多的线程可能会导致上一个线程对象创建了一半,此时实例不为null但是却不完整,下一个线程拿去用的话会导致报错;

/**  * @author : luobei  * @date : 2024/7/31 14:49  * 单例模式-双重验证懒汉式(线程安全)  */ public class SingletonLazy {     // 1.私有构造方法     private SingletonLazy(){}      // 2.在本类中创建私有静态的全局对象     private volatile static SingletonLazy instance;      // 3.提供一个全局访问点     public static SingletonLazy getInstance(){         //通过判断对象是否被初始化,来选择是否创建对象         if(instance == null){             synchronized (SingletonLazy.class){                 //抢到锁之后再次进行判断是否为null                 if(instance == null){                     instance = new SingletonLazy();                 }             }         }         return instance;     } }

 2.3 静态内部类

        既保证了线程安全,又可以懒加载,而且比双重验证懒汉式简洁

/**  * @author : luobei  * @date : 2024/7/31 14:49  * 单例模式-静态内部类  */ public class SingletonStatic {     // 1.私有构造方法     private SingletonStatic(){}      // 2.创建静态内部类     private static class SingletonHandler{         //在静态内部类中创建单例,在装载内部类的时候才会创建单例对象         private static  SingletonStatic instance = new SingletonStatic();     }      // 3.提供一个全局访问点     public static SingletonStatic getInstance(){         return SingletonHandler.instance;     } }

3.各种破坏单例模式的方法

3.1 反射

        即便构造方法私有化,通过反射依然能够创建实例;

Class singletonClass = SingletonStatic.class; Constructor declaredConstructor = singletonClass.getDeclaredConstructor(); declaredConstructor.setAccessible(true); SingletonStatic singletonStatic = declaredConstructor.newInstance();

3.1.1 解决办法

        在构造方法中添加验证

// 1.私有构造方法 private SingletonStatic(){     if(SingletonHandler.instance != null){         throw new RuntimeException("单例模式禁止反射调用");     } }

3.2 反序列化

//序列化对象输出流 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("tempFile.obj")); oos.writeObject(SingletonStatic.getInstance());  //序列化对象输入流 File file = new File("tempFile.obj"); ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file)); SingletonStatic singletonStatic = (SingletonStatic)ois.readObject();

         这是因为 readObject() -> readObject()  -> readObject0() -> checkResolve() ->  readOrdinaryObject(unshared)中的

 

3.2.1 解决办法 

        只需要在对象中添加 readResolve() 方法

private Object readResolve(){     return SingletonHandler.instance; }

         这是因为 readObject() -> readObject()  -> readObject0() -> checkResolve() ->  readOrdinaryObject(unshared)中的

3.3 总解决办法-枚举单例

/**  * @author : zluobei  * @date : 2024/7/31 14:49  * 单例模式-枚举  */ public enum SingletonEnum {     INSTANCE;      public static SingletonEnum getInstance(){         return INSTANCE;     } }

        首先能防止反射是因为 newInstance() -> newInstanceWithCaller() 里的

        反射在枚举上的应用有限,可以通过反射获取枚举类的信息和调用方法,但不能通过反射创建新的枚举实例。

        然后枚举类能防止序列化是因为,Java规范字规定每个枚举类型及其定义的枚举变量在JVM中都是唯一的,因此在枚举类型的序列化和反序列化上,Java做了特殊的规定:在序列化的时候Java仅仅将枚举对象的属性名字输到结果中,反序列化的时候则是通过java.lang.Enum的valueOf()方法来根据名字查找枚举对象。因此序列化前后的对象相同。

  • 序列化中对枚举有特殊处理,不会用到反射;

相关内容

热门资讯

盘点一款!卡农大厅辅助,菜鸟黑... 盘点一款!卡农大厅辅助,菜鸟黑桃a3作弊,法门教程(有挂攻略)1、下载好卡农大厅辅助正确养号方法之后...
技术分享!广东闲来辅助神器免费... 技术分享!广东闲来辅助神器免费版,捉住捣蛋鸡作弊,举措教程(有挂详情)1、超多福利:超高返利,海量正...
必知教程!仟众部落辅助,冰球突... 必知教程!仟众部落辅助,冰球突破辅助软件,方案教程(果真有挂)1、完成仟众部落辅助辅助器v3.3的残...
最新研发!吉祥填大坑有插件吗,... 最新研发!吉祥填大坑有插件吗,四川途游破解安装包,大纲教程(有挂细节)1、四川途游破解安装包脚本辅助...
一分钟揭秘!吉祥填大坑辅助器攻... 一分钟揭秘!吉祥填大坑辅助器攻略,指尖四川辅助破解版,练习教程(有挂工具)1、吉祥填大坑辅助器攻略有...
总算了解!凑一桌小程序辅助器,... 总算了解!凑一桌小程序辅助器,开心游戏快跑辅助,办法教程(有挂助手)1、任何凑一桌小程序辅助器透视是...
一起来讨论!开心庄园脚本辅助器... 一起来讨论!开心庄园脚本辅助器,全来潜山跑风辅助器,妙计教程(有挂技巧)1、玩家可以在全来潜山跑风辅...
每日必备!朋友局app辅助器下... 每日必备!朋友局app辅助器下载,爱玩娱乐暗堡辅助,机巧教程(真的有挂)1)朋友局app辅助器下载辅...
终于清楚!蜀山四川小程序破解版... 终于清楚!蜀山四川小程序破解版下载,拱趴大菠萝技巧,步骤教程(竟然有挂)1、不需要AI权限,帮助你快...
大神推荐!潮友会辅助器,多乐辅... 大神推荐!潮友会辅助器,多乐辅助器免费版,手册教程(有挂实锤)1)潮友会辅助器有没有挂:进一步探索潮...