第158天学习打卡(单例模式)

什么是设计模式

设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。

学习设计模式的意义

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

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

  • 可以提高程序员的思维能力、编程能力和设计能力

  • 使程序设计更加标准化、代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期。

  • 使设计的代码可重用性高,可读性强,可靠性高,灵活性好,可维护性强

设计模式的基本要素

  • 模式名称

  • 问题

  • 解决方案

  • 效果

GoF 23

  • 一种思维,一种态度,一种进步

创建型模式:

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

结构型模式:

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

行为型模式:

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

OOP(面向对象)七大原则

  • 开闭原则:对扩展开放,对修改关闭

  • 里氏替换原则:继承必须确保超类所拥有的性质在子类中仍然成立

  • 依赖倒置原则:要面向接口编程,不要面向实现编程

  • 单一职责原则:控制类的粒度大小,将对象解耦,提高其内聚性

  • 接口隔离原则: 要为各个类建立它们需要的专用接口

  • 迪米特法则:只与你的直接朋友交谈,不跟“陌生人”说话

  • 合成复用原则:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现

单例模式(构造器一定要私有)

饿汉式

 package com.doudou.note.single;
 //饿汉式单例
 public class Hungry {
     //可能会浪费空间
     private byte[] date1 = new byte[1024*1024];
     private byte[] date2 = new byte[1024*1024];
     private byte[] date3 = new byte[1024*1024];
     private byte[] date4 = new byte[1024*1024];
 
     private  Hungry(){
 
    }
     private final static Hungry HUNGRY = new Hungry();
 
     public static Hungry getInstance(){
         return HUNGRY;
    }
 }
 

懒汉式

 package com.doudou.note.single;
 //懒汉式单例
 public class LazyMan {
     private LazyMan(){
         //构造器私有
         System.out.println(Thread.currentThread().getName() + "ok");
    }
     private static LazyMan lazyMan;
 
     public static LazyMan getInstance(){
         if(lazyMan == null){
             lazyMan = new LazyMan();
        }
         return lazyMan;
    }
 
     //单例模式下是ok的
     //多线程并发 会出现问题
     public static void main(String[] args){
         for (int i = 0; i < 10; i++) {
             new Thread(()->{
                 LazyMan.getInstance();
            }).start();
 
        }
    }
 }
 

输出结果:

 Thread-0ok
 Thread-3ok
 Thread-1ok
 Thread-2ok
 
 Process finished with exit code 0

DCL懒汉式

 package com.doudou.note.single;
 //懒汉式单例
 public class LazyMan {
     private LazyMan(){
         //构造器私有
         System.out.println(Thread.currentThread().getName() + "ok");
    }
     private static LazyMan lazyMan;
 //双重检测锁模式的 懒汉式单例 DCL懒汉式
     public static LazyMan getInstance(){
         if(lazyMan==null){
             synchronized (LazyMan.class){//锁class只有一个
                 if(lazyMan == null){
                     lazyMan = new LazyMan();
                }
            }
        }
 
         return lazyMan;
    }
 
     //单例模式下是ok的
     //多线程并发 会出现问题
     public static void main(String[] args){
         for (int i = 0; i < 10; i++) {
             new Thread(()->{
                 LazyMan.getInstance();
            }).start();
 
        }
    }
 }
 

输出结果:

 Thread-0ok
 
 Process finished with exit code 0
 

加入volatile实现双重指令重排和原子性操作

 package com.doudou.note.single;
 //懒汉式单例
 public class LazyMan {
     private LazyMan(){
         //构造器私有
         System.out.println(Thread.currentThread().getName() + "ok");
    }
 
     //volatile可以实现双重指令重排和实现原子性操作
     private volatile static LazyMan lazyMan;
 
     //双重检测锁模式的 懒汉式单例 DCL懒汉式
     public static LazyMan getInstance(){
         if(lazyMan==null){
             synchronized (LazyMan.class){//锁class只有一个
                 if(lazyMan == null){
                     lazyMan = new LazyMan();//不是一个原子性操作
                     /**
                      * 1.分配内存空间
                      * 2.执行构造方法,初始化对象
                      * 3.把这个对象指向这个空间
                      *
                      * 比如出现指令重排现象
                      * 原本应该执行 123 可能出现132 如果A线程执行了132 B此时进来 会认为lazyMan还没有完成构造
                      */
                }
            }
        }
 
         return lazyMan;
    }
 
 
 }
 

静态内部类

 package com.doudou.note.single;
 //静态内部类
 public class Holder {
     private Holder(){
 
    }
     public static Holder getInstance(){
         return InnerClass.HOLDER;
    }
 
     public static class InnerClass{
         private static final  Holder HOLDER = new Holder();
    }
 }
 

单例不安全,存在反射

反射可以破坏单例

 package com.doudou.note.single;
 
 import java.lang.reflect.Constructor;
 
 //懒汉式单例
 public class LazyMan {
     private LazyMan(){
         //构造器私有
         System.out.println(Thread.currentThread().getName() + "ok");
    }
 
     //volatile可以实现双重指令重排和实现原子性操作
     private volatile static LazyMan lazyMan;
 
     //双重检测锁模式的 懒汉式单例 DCL懒汉式
     public static LazyMan getInstance(){
         if(lazyMan==null){
             synchronized (LazyMan.class){//锁class只有一个
                 if(lazyMan == null){
                     lazyMan = new LazyMan();//不是一个原子性操作
                     /**
                      * 1.分配内存空间
                      * 2.执行构造方法,初始化对象
                      * 3.把这个对象指向这个空间
                      *
                      * 比如出现指令重排现象
                      * 原本应该执行 123 可能出现132 如果A线程执行了132 B此时进来 会认为lazyMan还没有完成构造
                      */
                }
            }
        }
 
         return lazyMan;
    }
     //反射
     public static void main(String[] args) throws Exception {
         LazyMan instance = LazyMan.getInstance();
         Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器
         declaredConstructor.setAccessible(true);
         LazyMan instance1 = declaredConstructor.newInstance();
 
         System.out.println(instance);
         System.out.println(instance1);
 
    }
 
 
 }
 

输出结果:

 mainok
 mainok
 com.doudou.note.single.LazyMan@14ae5a5
 com.doudou.note.single.LazyMan@7f31245a
 
 Process finished with exit code 0

破坏反射

 package com.doudou.note.single;
 
 import java.lang.reflect.Constructor;
 
 //懒汉式单例
 public class LazyMan {
     private LazyMan(){
         //构造器私有
         synchronized (LazyMan.class){
             if (lazyMan!=null){
                 throw new RuntimeException("不要试图使用反射破坏异常");
            }
        }
 
 
    }
 
     //volatile可以实现双重指令重排和实现原子性操作
     private volatile static LazyMan lazyMan;
 
     //双重检测锁模式的 懒汉式单例 DCL懒汉式
     public static LazyMan getInstance(){
         if(lazyMan==null){
             synchronized (LazyMan.class){//锁class只有一个
                 if(lazyMan == null){
                     lazyMan = new LazyMan();//不是一个原子性操作
                     /**
                      * 1.分配内存空间
                      * 2.执行构造方法,初始化对象
                      * 3.把这个对象指向这个空间
                      *
                      * 比如出现指令重排现象
                      * 原本应该执行 123 可能出现132 如果A线程执行了132 B此时进来 会认为lazyMan还没有完成构造
                      */
                }
            }
        }
 
         return lazyMan;
    }
     //反射
     public static void main(String[] args) throws Exception {
         LazyMan instance = LazyMan.getInstance();
         Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器
         declaredConstructor.setAccessible(true);
         LazyMan instance1 = declaredConstructor.newInstance();
 
         System.out.println(instance);
         System.out.println(instance1);
 
    }
 
 
 }
 

输出结果

 Exception in thread "main" java.lang.reflect.InvocationTargetException
  at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
  at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
  at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
  at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
  at com.doudou.note.single.LazyMan.main(LazyMan.java:46)
 Caused by: java.lang.RuntimeException: 不要试图使用反射破坏异常
  at com.doudou.note.single.LazyMan.<init>(LazyMan.java:11)
  ... 5 more
 
 Process finished with exit code 1

破坏单例模式

 package com.doudou.note.single;
 
 import java.lang.reflect.Constructor;
 
 //懒汉式单例
 public class LazyMan {
     private LazyMan(){
         //构造器私有
         synchronized (LazyMan.class){
             if (lazyMan!=null){
                 throw new RuntimeException("不要试图使用反射破坏异常");
            }
        }
 
 
    }
 
     //volatile可以实现双重指令重排和实现原子性操作
     private volatile static LazyMan lazyMan;
 
     //双重检测锁模式的 懒汉式单例 DCL懒汉式
     public static LazyMan getInstance(){
         if(lazyMan==null){
             synchronized (LazyMan.class){//锁class只有一个
                 if(lazyMan == null){
                     lazyMan = new LazyMan();//不是一个原子性操作
                     /**
                      * 1.分配内存空间
                      * 2.执行构造方法,初始化对象
                      * 3.把这个对象指向这个空间
                      *
                      * 比如出现指令重排现象
                      * 原本应该执行 123 可能出现132 如果A线程执行了132 B此时进来 会认为lazyMan还没有完成构造
                      */
                }
            }
        }
 
         return lazyMan;
    }
     //反射
     public static void main(String[] args) throws Exception {
         // LazyMan instance = LazyMan.getInstance();
         Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器
         declaredConstructor.setAccessible(true);
         LazyMan instance = declaredConstructor.newInstance();
         LazyMan instance1 = declaredConstructor.newInstance();
 
         System.out.println(instance);
         System.out.println(instance1);
 
    }
 
 
 }
 

输出结果

 com.doudou.note.single.LazyMan@14ae5a5
 com.doudou.note.single.LazyMan@7f31245a
 
 Process finished with exit code 0

破坏反射2

 package com.doudou.note.single;
 
 import java.lang.reflect.Constructor;
 
 //懒汉式单例
 public class LazyMan {
     private static boolean qinjiang = false;
     private LazyMan(){
         //构造器私有
         synchronized (LazyMan.class){
             if (qinjiang == false){
                 qinjiang = true;
 
            }else{
                 throw new RuntimeException("不要试图使用反射破话异常");
            }
        }
 
 
    }
 
     //volatile可以实现双重指令重排和实现原子性操作
     private volatile static LazyMan lazyMan;
 
     //双重检测锁模式的 懒汉式单例 DCL懒汉式
     public static LazyMan getInstance(){
         if(lazyMan==null){
             synchronized (LazyMan.class){//锁class只有一个
                 if(lazyMan == null){
                     lazyMan = new LazyMan();//不是一个原子性操作
                     /**
                      * 1.分配内存空间
                      * 2.执行构造方法,初始化对象
                      * 3.把这个对象指向这个空间
                      *
                      * 比如出现指令重排现象
                      * 原本应该执行 123 可能出现132 如果A线程执行了132 B此时进来 会认为lazyMan还没有完成构造
                      */
                }
            }
        }
 
         return lazyMan;
    }
     //反射
     public static void main(String[] args) throws Exception {
         // LazyMan instance = LazyMan.getInstance();
         Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器
         declaredConstructor.setAccessible(true);
         LazyMan instance = declaredConstructor.newInstance();
         LazyMan instance1 = declaredConstructor.newInstance();
 
         System.out.println(instance);
         System.out.println(instance1);
 
    }
 
 
 }
 

输出结果

 Exception in thread "main" java.lang.reflect.InvocationTargetException
  at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
  at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
  at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
  at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
  at com.doudou.note.single.LazyMan.main(LazyMan.java:51)
 Caused by: java.lang.RuntimeException: 不要试图使用反射破话异常
  at com.doudou.note.single.LazyMan.<init>(LazyMan.java:15)
  ... 5 more
 
 Process finished with exit code 1
 

破坏单例

 package com.doudou.note.single;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 
 //懒汉式单例
 public class LazyMan {
     private static boolean qinjiang = false;
     private LazyMan(){
         //构造器私有
         synchronized (LazyMan.class){
             if (qinjiang == false){
                 qinjiang = true;
 
            }else{
                 throw new RuntimeException("不要试图使用反射破话异常");
            }
        }
 
 
    }
 
     //volatile可以实现双重指令重排和实现原子性操作
     private volatile static LazyMan lazyMan;
 
     //双重检测锁模式的 懒汉式单例 DCL懒汉式
     public static LazyMan getInstance(){
         if(lazyMan==null){
             synchronized (LazyMan.class){//锁class只有一个
                 if(lazyMan == null){
                     lazyMan = new LazyMan();//不是一个原子性操作
                     /**
                      * 1.分配内存空间
                      * 2.执行构造方法,初始化对象
                      * 3.把这个对象指向这个空间
                      *
                      * 比如出现指令重排现象
                      * 原本应该执行 123 可能出现132 如果A线程执行了132 B此时进来 会认为lazyMan还没有完成构造
                      */
                }
            }
        }
 
         return lazyMan;
    }
     //反射
     public static void main(String[] args) throws Exception {
         // LazyMan instance = LazyMan.getInstance();
 
         Field qinjiang = LazyMan.class.getDeclaredField("qinjiang");
         qinjiang.setAccessible(true);
 
         Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);//获得空参构造器
         declaredConstructor.setAccessible(true);
         LazyMan instance = declaredConstructor.newInstance();
 
         qinjiang.set(instance, false);//第一个对象的值再改为false
         LazyMan instance1 = declaredConstructor.newInstance();
 
         System.out.println(instance);
         System.out.println(instance1);
 
    }
 
 
 }
 

结果

 com.doudou.note.single.LazyMan@7f31245a
 com.doudou.note.single.LazyMan@6d6f6e28
 
 Process finished with exit code 0

枚举(反射不能破坏枚举)

 package com.doudou.note.single;
 //enum 是一个什么? 枚举本身也是一个Class类
 
 
 
 public enum EnumSingle {
 
     INSTANCE;
     public EnumSingle getInstance(){
         return  INSTANCE;
    }
 
 
 }
 class Test{
     public static void main(String[] args) {
         EnumSingle instance1 = EnumSingle.INSTANCE;
         EnumSingle instance2 = EnumSingle.INSTANCE;
 
         System.out.println(instance1);
         System.out.println(instance2);
 
    }
 }
 

输出结果:

 INSTANCE
 INSTANCE
 
 Process finished with exit code 0

反射不能破坏枚举证明

 package com.doudou.note.single;
 //enum 是一个什么? 枚举本身也是一个Class类
 
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 
 public enum EnumSingle {
 
     INSTANCE;
     public EnumSingle getInstance(){
         return  INSTANCE;
    }
 
 
 }
 class Test{
     public static void main(String[] args) throws Exception {
         EnumSingle instance1 = EnumSingle.INSTANCE;
         Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
         declaredConstructor.setAccessible(true);//私有权限进行破除
         EnumSingle instance2 = declaredConstructor.newInstance();
 
         //NoSuchMethodException: com.doudou.note.single.EnumSingle.<init>()
         System.out.println(instance1);
         System.out.println(instance2);
 
 
    }
 }
 
 

输出结果(发现证明失败):

 Exception in thread "main" java.lang.NoSuchMethodException: com.doudou.note.single.EnumSingle.<init>()
  at java.lang.Class.getConstructor0(Class.java:3082)
  at java.lang.Class.getDeclaredConstructor(Class.java:2178)
  at com.doudou.note.single.Test.main(EnumSingle.java:20)

对枚举的class文件进行编译

image-20210615212856023

 

把class文件反编译为java文件,首先把jad.exe复制到这个目录下

image-20210615213605303

查看反编译的java文件可以发现枚举确实不能破坏单例

 package com.doudou.note.single;
 //enum 是一个什么? 枚举本身也是一个Class类
 
 
 import java.lang.reflect.Constructor;
 public enum EnumSingle {
 
     INSTANCE;
     public EnumSingle getInstance(){
         return  INSTANCE;
    }
 
 
 }
 class Test{
     public static void main(String[] args) throws Exception {
         EnumSingle instance1 = EnumSingle.INSTANCE;
         Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class, int.class);
         declaredConstructor.setAccessible(true);//私有权限进行破除
         EnumSingle instance2 = declaredConstructor.newInstance();
 
         //NoSuchMethodException: com.doudou.note.single.EnumSingle.<init>()
         System.out.println(instance1);
         System.out.println(instance2);
 
 
    }
 }
 

结果(这个说明反射不能破坏枚举):

 Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
  at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
  at com.doudou.note.single.Test.main(EnumSingle.java:20)
 
 Process finished with exit code 1

枚举的最终反编译源码(发现枚举没有无参构造 只有有参构造 而且有两个参数)

 // Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
 // Jad home page: http://www.kpdus.com/jad.html
 // Decompiler options: packimports(3)
 // Source File Name:   EnumSingle.java
 
 package com.doudou.note.single;
 
 
 public final class EnumSingle extends Enum
 {
 
     public static EnumSingle[] values()
    {
         return (EnumSingle[])$VALUES.clone();
    }
 
     public static EnumSingle valueOf(String name)
    {
         return (EnumSingle)Enum.valueOf(com/doudou/note/single/EnumSingle, name);
    }
 
     private EnumSingle(String s, int i)
    {
         super(s, i);
    }
 
     public EnumSingle getInstance()
    {
         return INSTANCE;
    }
 
     public static final EnumSingle INSTANCE;
     private static final EnumSingle $VALUES[];
 
     static
    {
         INSTANCE = new EnumSingle("INSTANCE", 0);
         $VALUES = (new EnumSingle[] {
             INSTANCE
        });
    }
 }
 

B站学习网址:【狂神说Java】单例模式-23种设计模式系列哔哩哔哩bilibili

posted @ 2021-06-15 21:58  豆豆tj  阅读(65)  评论(0)    收藏  举报