23种设计模式之单例模式
创始人
2024-06-03 14:58:53
0

设计模式之单例模式

    • 一、23种设计模式
      • 1. 概念
      • 2. 意义
    • 二、GoF 23
    • 三、OOP七大原则
    • 四、单例模式
      • 1. 饿汉式单例
      • 2. 懒汉式单例
      • 3. DCL 懒汉式单例
      • 4. 静态内部类单例
      • 5. 枚举单例
      • 6. 单例不安全,反射破坏

一、23种设计模式

1. 概念


  • 设计模式是前辈们对代码开发经验的总结,是解决特定问题的一系列套路,他不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案
  • 1995年,GoF合作出版了《设计模式:可复用面向对象软件的基础》一书,收录了23中设计模式,从此树立了软件设计模式领域的里程碑,人称GoF设计模式

2. 意义

学习设计模式的意义

  • 设计模式的本质是面向对象设计原则的实际运用,是对类的封装性继承性多态性以及类的关联关系和组合关系的充分理解
  • 正确使用设计模式具有以下优点:
    • 可以提高程序员的思维能力编程能力设计能力
    • 使程序设计更加标准化、代码编制更加工程化、使软件开发效率大大提高,从而缩短软件的开发周期
  • 使设计的代码可重用性高可读性强可靠性高灵活性好可维护性强

二、GoF 23


  • GoF23一种思维,一种态度,一种进步
  • 创建型模式:单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式
  • 结构性模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式
  • 行为性模式:模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式

三、OOP七大原则


  • 开闭原则:对扩展开放,对修改关闭
  • 里氏替换原则:继承必须确保父类所拥有的性质在子类中依然成立
  • 依赖倒置原则:要面向接口编程,不要面向实现编程
  • 职责原则:控制类的粒度大小,将对象解耦,提高其内聚性
  • 接口隔离原则:要为各个类建立它们需要的专用接口
  • 迪米特原则:只与你的直接朋友(成员变量、方法参数、方法返回值中的类)交谈,不跟“陌生人”(方法体内部的类)说话
  • 合成复用原则:尽量先使用组合或者聚合等关联关系实现,其次才考虑使用继承关系来实现

四、单例模式

1. 饿汉式单例


// 饿汉式单例
public class Hungry {// 可能会很浪费空间private byte[] data1 = new byte[1024*1024];private byte[] data2 = new byte[1024*1024];private byte[] data3 = new byte[1024*1024];private byte[] data4 = new byte[1024*1024];private byte[] data5 = new byte[1024*1024];// 私有化构造方法private Hungry(){}private final static Hungry HUNGRY = new Hungry();// 对外提供一个开放的接口,用于返回实例对象public static Hungry getInstance(){return HUNGRY;}}

2. 懒汉式单例


存在多线程并发模式,后面的DCL懒汉式解决并发问题

public class LazyMan {private LazyMan(){System.out.println(Thread.currentThread().getName()+"ok");}private static LazyMan lazyMan;// 双重检测锁模式的 懒汉式单例 DCL懒汉式public static LazyMan getInstance(){if(lazyMan == null){lazyMan = new LazyMan();// 不是一个原子性操作}return lazyMan;}/** lazyMan = new LazyMan();* 1. 分配内存空间* 2. 执行构造方法,初始化对象* 3. 把这个对象指向这个空间** 123* 132 A*     B // 此时lazyMan还没有完成构造* */// 多线程并发public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Thread(()->{LazyMan instance = LazyMan.getInstance();System.out.println(instance.hashCode());}).start();}}
}

3. DCL 懒汉式单例


注意synchronized解决并发问题,但是因为lazyMan = new LazyMan(); 不是原子性操作(可以分割,见代码注释),可能发生指令重排序的问题,通过 volatile 来解决

  • Java语言提供了 volatilesynchronized 两个关键字来保证线程之间操作的有序性volatile 是因为其本身包含“禁止指令重排序”的语义,synchronized 是由“一个变量在同一个时刻只允许一条线程对其进行lock操作”这条规则获得的,此规则决定了持有同一个对象锁的两个同步块只能串行执行。
  • 原子性就是指该操作是不可再分的。不论是多核还是单核,具有原子性的量,同一时刻只能有一个线程来对它进行操作。简而言之,在整个操作过程中不会被线程调度器中断的操作,都可认为是原子性。比如 a = 1;
public class LazyMan {// 私有化构造器private LazyMan(){System.out.println(Thread.currentThread().getName()+"ok");}private static volatile LazyMan lazyMan;// Double Check Lock 双重检测锁模式的懒汉式单例public static LazyMan getInstance(){if( lazyMan == null ){synchronized (LazyMan.class){  //  synchronized加锁解决多线程下的问题if(lazyMan == null){lazyMan = new LazyMan();}}}return lazyMan;}// 多线程并发public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Thread(()->{LazyMan instance = LazyMan.getInstance();System.out.println(instance.hashCode());}).start();}}
}

4. 静态内部类单例


// 静态内部类
public class Holder {// 构造器私有private Holder(){}public static Holder getInstance(){return InnerClass.HOLDER;}public static class InnerClass{private static final  Holder HOLDER = new Holder();}}

5. 枚举单例


public enum EnumSingle {INSTANCE;public EnumSingle getInstance(){return INSTANCE;}
}class Test01{public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {EnumSingle instance = EnumSingle.INSTANCE;Constructor declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class, int.class);declaredConstructor.setAccessible(true);EnumSingle enumSingle = declaredConstructor.newInstance();System.out.println(instance);System.out.println(enumSingle);}
}

注意: 枚举的唯一性,无法通过反射的方式创建枚举对象
Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects at java.lang.reflect.Constructor.newInstance(Constructor.java:417) at com.ppt.design.Test01.main(EnumSingle.java:18)

6. 单例不安全,反射破坏


public class LazyMan {private static boolean flag = false; // 红绿灯解决通过反射创建对象(反编译可以破解该方法)// 私有化构造器private LazyMan(){synchronized (LazyMan.class){if(flag == false){flag = true;}else{throw new RuntimeException("不要试图使用反射破坏单例");}}System.out.println(Thread.currentThread().getName()+"ok");}private static volatile LazyMan lazyMan;// Double Check Lock 双重检测锁模式的懒汉式单例public static LazyMan getInstance(){if (lazyMan == null) {lazyMan = new LazyMan();}return lazyMan;}// 反射public static void main(String[] args) throws Exception {// LazyMan instance = LazyMan.getInstance();Field flag = LazyMan.class.getDeclaredField("flag");flag.setAccessible(true);Constructor declaredConstructor = LazyMan.class.getDeclaredConstructor(null);declaredConstructor.setAccessible(true); // 无视私有的构造器LazyMan lazyMan = declaredConstructor.newInstance();flag.set(lazyMan,false);System.out.println(lazyMan.hashCode());LazyMan lazyMan1 = declaredConstructor.newInstance();System.out.println(lazyMan1.hashCode());}
}

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...