设计模式1 | 青训营笔记

101 阅读6分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记

了解设计模式

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

学习设计模式的意义

  • 设计模式的本质是面向对象设计原则的实际运用, 是对类的封装性, 继承性和多态性以及类的关联关系和组合关系的充分理解

  • 正确使用设计模式具有以下优点:

    • 可以提高程序员的思维能力, 编程能力和设计能力
    • 使程序设计更加标准化, 代码编制更加工程化, 使软件开发效率大大提高, 从而缩短软件的开发周期
    • 使设计的代码可重用性高, 可读性强, 可靠性高, 灵活性好, 可维护性强

GoF 23

  • Gof23

    • 一种思维, 一种态度, 一种进步
  • 创建型模式:

    • 单例模式, 工厂模式, 抽象工厂模式, 建造者模式, 原型模式
  • 结构型模式:

    • 适配器模式, 桥接模式, 装饰模式, 组合模式, 外观模式, 享元模式, 代理模式
  • 行为型模式:

    • 模板方法模式, 命令模式, 迭代器模式, 观察者模式, 中介者模式, 备忘录模式, 解释器模式, 状态模式, 策略模式, 职责链模式, 访问者模式

OOP 七大原则

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

单例模式

饿汉式、DCL懒汉式,深究

饿汉式

 package com.kuang.single;
 //单例模式构造器一定要私有
 // 饿汉式 单例
 public class Hungry {
 ​
     int id;
     String name;
 ​
     //可能会浪费空间
     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];
     //构造器私有  就不能通过new来创建对象
     private Hungry() {
     }
 ​
     private final static Hungry HUNGRY = new Hungry();
 ​
     public static Hungry getInstance(){
         return HUNGRY;
     }
 ​
     public static void main(String[] args) {
         Hungry instance = getInstance();
         byte[] data1 = instance.data1;
         int id = instance.id;
         String name = instance.name;
     }
 }

懒汉式

存在多线程并发模式,后面的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;
     }
     /*
     * 1.分配内存空间
     * 2、执行构造方法,初始化对象
     * 3、把这个对象指向者个空间
     *
     * 123
     * 132 A
     *
     *     B //此时lazyMan还没有完成构造
     *
     * */
 ​
 ​
     //多线程并发
     public static void main(String[] args) {
         for (int i = 0; i < 10; i++) {
             new Thread(()->{
                 LazyMan.getInstance();
             }).start();
         }
     }
 }
 ​

DCL懒汉式

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

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

静态内部类

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

单例不安全,反射破坏(见注释及main方法中反射破解步骤)

 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 ​
 //单例懒汉式
 //懒汉式单例
 ​
 public class LazyMan {
     private static boolean qingjiang = false;//红绿等解决通过反射创建对象(反编译可以破解该方法)
     private LazyMan(){
         synchronized (LazyMan.class){
             if (qingjiang==false){
                 qingjiang = true;
             }else{
                 throw new RuntimeException("不要试图使用反射破坏单例");
             }
 ​
         }
         System.out.println(Thread.currentThread().getName()+"OK");
     }
 ​
     private volatile static LazyMan lazyMan;//volatile避免指令重排
 ​
 ​
     //双重检测锁模式的  懒汉式单例    DCL懒汉式
     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 qingjiang = LazyMan.class.getDeclaredField("qingjiang");
     qingjiang.setAccessible(true);
 ​
     Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
     declaredConstructor.setAccessible(true);//无视私有的构造器
     LazyMan instance1 = declaredConstructor.newInstance();
     qingjiang.set(instance1,false);
     System.out.println(instance1);
     LazyMan instance2 = declaredConstructor.newInstance();
 ​
     System.out.println(instance2);
 ​
 }
 ​
     /*
     * 1.分配内存空间
     * 2、执行构造方法,初始化对象
     * 3、把这个对象指向者个空间
     *
     * 123
     * 132 A
     *
     *     B //此时lazyMan还没有完成构造
     *
     * */
 ​
 ​
     //多线程并发
    /* public static void main(String[] args) {
         for (int i = 0; i < 10; i++) {
             new Thread(()->{
                 LazyMan.getInstance();
             }).start();
         }
     }*/
 }
 ​

枚举: 通过反射破解枚举发现不成功: 1、普通的反编译会欺骗开发者,说enum枚举是无参构造 2、实际enum为有参构造(见后面); 3、通过反射破解枚举会发现抛出异常 Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects at java.lang.reflect.Constructor.newInstance(Constructor.java:417) at com.ph.single.Test.main(EnumSingle.java:19)

 import java.lang.reflect.Constructor;
 ​
 //enmu是什么?本身也是一个class类
 public enum EnumSingle {
     INSTANCE;
     public EnumSingle getInstance(){
         return INSTANCE;
     }
 }
 ​
 class Test{
     public static void main(String[] args) throws Exception {
         EnumSingle instance = EnumSingle.INSTANCE;
         Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
         declaredConstructor.setAccessible(true);
         EnumSingle instance2 = declaredConstructor.newInstance();
 ​
         //java.lang.NoSuchMethodException: com.ph.single.EnumSingle.<init>()
         System.out.println(instance);
         System.out.println(instance2);
 ​
     }
 }
 ​

发现枚举是有参构造

\