【设计模式1】单例模式

【设计模式1】单例模式
【设计模式2】工厂模式
【设计模式3】原型模式
【设计模式4】建造者模式

1,懒汉模式:

延迟加载,只有在真正使用的时候,才开始实例化;

  1. 线程安全问题
  2. double check 枷锁优化
  3. 编译器(JIT),CPU可能会对指令进行重重新排序,导致使用到尚未初始化的实力,可以通过添加volatile关键字机进行修饰,对于volatile修饰的字段,可以防止指令重排序以及内存立马可见;

推荐使用的代码示例:

public class SingletonDemo {
    private volatile  static  SingletonDemo instance;
    private SingletonDemo(){ }
    public static SingletonDemo getInstance(){
        if(instance==null){
            synchronized (SingletonDemo.class){
                if(instance==null){
                    instance=new SingletonDemo();
                }
            }
        }
        return instance;
    }
}

Spring中的应用:

package org.springframework.core

public class ReactiveAdapterRegistry {

   @Nullable
   private static volatile ReactiveAdapterRegistry sharedInstance;


   /**
    * Return a shared default {@code ReactiveAdapterRegistry} instance,
    * lazily building it once needed.
    * <p><b>NOTE:</b> We highly recommend passing a long-lived, pre-configured
    * {@code ReactiveAdapterRegistry} instance for customization purposes.
    * This accessor is only meant as a fallback for code paths that want to
    * fall back on a default instance if one isn't provided.
    * @return the shared {@code ReactiveAdapterRegistry} instance
    * @since 5.0.2
    */
   public static ReactiveAdapterRegistry getSharedInstance() {
      ReactiveAdapterRegistry registry = sharedInstance;
      if (registry == null) {
         synchronized (ReactiveAdapterRegistry.class) {
            registry = sharedInstance;
            if (registry == null) {
               registry = new ReactiveAdapterRegistry();
               sharedInstance = registry;
            }
         }
      }
      return registry;
   }
}

2,饿汉模式:

类加载的初始化阶段完成了实例的初始化。本质上是借助JVM类加载机制,保证实例的唯一性。
类加载过程:
1,加载二进制数据到内存中,生成对应的Class数据结构;
2,连接:a:验证,b:准备(给类的成员变量赋默认值),c:解析
3,初始化:给类的金泰变量赋予初始值
只有在真正使用对应的类时,才会触发初始化(当前类是启动类即main函数所在类,直接进行new操作,访问静态属性,访问静态方法,用反射访问类,初始化一个类的子类等)
示例代码:

public class SingletonDemo {
    //在类的内部,设置静态属性,并初始化该对象,这样在类的加载过程中,即可初始化该对象;
    private static SingletonDemo instance=new SingletonDemo();
    private SingletonDemo(){

    }
    public static SingletonDemo getInstance(){
        return instance;
    }
}

源码中的应用:java.lang.Runtime

public class Runtime {
    private static final Runtime currentRuntime = new Runtime();

    private static Version version;

    /**
     * Returns the runtime object associated with the current Java application.
     * Most of the methods of class {@code Runtime} are instance
     * methods and must be invoked with respect to the current runtime object.
     *
     * @return  the {@code Runtime} object associated with the current
     *          Java application.
     */
    public static Runtime getRuntime() {
        return currentRuntime;
    }

    /** Don't let anyone else instantiate this class */
    private Runtime() {}
    
}

3,静态内部类

本质上是利用类的加载机制保证线程安全;
只有在实际使用的时候,才会触发类的初始化,所以也是一种懒汉模式;因为静态内部类的加载不需要依附外部类,在使用时才加载。不过在加载静态内部类的过程中也会加载外部类。
代码示例:

public class Singleton {

    /**
     * 1.构造私有化
     */
    private Singleton() {
    }
    /**
     * 2.构建静态内部类
     */
    private static class InsideSingleton {
        private static final Singleton SINGLETON = new Singleton();
    }

    /**
     * 3.提供公共方法获取实例
     *  调用该方法时才会加载静态内部类
     * @return
     */
    public static Singleton getInstance() {
        return InsideSingleton.SINGLETON;
    }
}

4,通过枚举实现单例模式

public enum SingletonEnumV2 {
    INSTANCE;

    private SingletonEnumV2() {
    }

    public void doSomething() {
        System.out.println("doSomething");
    }
}

编译Singleton类,并通过javap命令查看该类的字节码信息,虽然只有几行代码,但是jvm会编译的字节码如下:

javap -v -p SingletonEnumV2.class
Classfile /D:/project/nhw-project/nhw-java/target/classes/design/SingletonEnumV2.class
  Last modified 2022年1月12日; size 1120 bytes
  SHA-256 checksum 26555b703aa815d5d3af15b2104e32070ae6e30d1372b372bf7ca89faa0602b1
  Compiled from "SingletonEnumV2.java"
  //会生成一个final 类 SingletonEnumV2 并且会继承Enum,所以枚举实际也是java 类
public final class design.SingletonEnumV2 extends java.lang.Enum<design.SingletonEnumV2>
  minor version: 0
  major version: 52
  flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
  this_class: #2                          // design/SingletonEnumV2
  super_class: #13                        // java/lang/Enum
  interfaces: 0, fields: 2, methods: 5, attributes: 2
  //常量池:
Constant pool:
   #1 = Fieldref           #2.#3          // design/SingletonEnumV2.$VALUES:[Ldesign/SingletonEnumV2;
   #2 = Class              #4             // design/SingletonEnumV2
   #3 = NameAndType        #5:#6          // $VALUES:[Ldesign/SingletonEnumV2;
   #4 = Utf8               design/SingletonEnumV2
   #5 = Utf8               $VALUES
   #6 = Utf8               [Ldesign/SingletonEnumV2;
   #7 = Methodref          #8.#9          // "[Ldesign/SingletonEnumV2;".clone:()Ljava/lang/Object;
   #8 = Class              #6             // "[Ldesign/SingletonEnumV2;"
   #9 = NameAndType        #10:#11        // clone:()Ljava/lang/Object;
  #10 = Utf8               clone
  #11 = Utf8               ()Ljava/lang/Object;
  #12 = Methodref          #13.#14        // java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
  #13 = Class              #15            // java/lang/Enum
  #14 = NameAndType        #16:#17        // valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
  #15 = Utf8               java/lang/Enum
  #16 = Utf8               valueOf
  #17 = Utf8               (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
  #18 = Methodref          #13.#19        // java/lang/Enum."<init>":(Ljava/lang/String;I)V
  #19 = NameAndType        #20:#21        // "<init>":(Ljava/lang/String;I)V
  #20 = Utf8               <init>
  #21 = Utf8               (Ljava/lang/String;I)V
  #22 = Fieldref           #23.#24        // java/lang/System.out:Ljava/io/PrintStream;
  #23 = Class              #25            // java/lang/System
  #24 = NameAndType        #26:#27        // out:Ljava/io/PrintStream;
  #25 = Utf8               java/lang/System
  #26 = Utf8               out
  #27 = Utf8               Ljava/io/PrintStream;
  #28 = String             #29            // doSomething
  #29 = Utf8               doSomething
  #30 = Methodref          #31.#32        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #31 = Class              #33            // java/io/PrintStream
  #32 = NameAndType        #34:#35        // println:(Ljava/lang/String;)V
  #33 = Utf8               java/io/PrintStream
  #34 = Utf8               println
  #35 = Utf8               (Ljava/lang/String;)V
  #36 = String             #37            // INSTANCE
  #37 = Utf8               INSTANCE
  #38 = Methodref          #2.#19         // design/SingletonEnumV2."<init>":(Ljava/lang/String;I)V
  #39 = Fieldref           #2.#40         // design/SingletonEnumV2.INSTANCE:Ldesign/SingletonEnumV2;
  #40 = NameAndType        #37:#41        // INSTANCE:Ldesign/SingletonEnumV2;
  #41 = Utf8               Ldesign/SingletonEnumV2;
  #42 = Utf8               values
  #43 = Utf8               ()[Ldesign/SingletonEnumV2;
  #44 = Utf8               Code
  #45 = Utf8               LineNumberTable
  #46 = Utf8               (Ljava/lang/String;)Ldesign/SingletonEnumV2;
  #47 = Utf8               LocalVariableTable
  #48 = Utf8               name
  #49 = Utf8               Ljava/lang/String;
  #50 = Utf8               this
  #51 = Utf8               Signature
  #52 = Utf8               ()V
  #53 = Utf8               <clinit>
  #54 = Utf8               Ljava/lang/Enum<Ldesign/SingletonEnumV2;>;
  #55 = Utf8               SourceFile
  #56 = Utf8               SingletonEnumV2.java
{
    //SingletonEnumV2  类的静态属性;
  public static final design.SingletonEnumV2 INSTANCE;
    descriptor: Ldesign/SingletonEnumV2;
    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

  private static final design.SingletonEnumV2[] $VALUES;
    descriptor: [Ldesign/SingletonEnumV2;
    flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC

  public static design.SingletonEnumV2[] values();
    descriptor: ()[Ldesign/SingletonEnumV2;
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: getstatic     #1                  // Field $VALUES:[Ldesign/SingletonEnumV2;
         3: invokevirtual #7                  // Method "[Ldesign/SingletonEnumV2;".clone:()Ljava/lang/Object;
         6: checkcast     #8                  // class "[Ldesign/SingletonEnumV2;"
         9: areturn
      LineNumberTable:
        line 3: 0
 //静态的valueOf方法
  public static design.SingletonEnumV2 valueOf(java.lang.String);
    descriptor: (Ljava/lang/String;)Ldesign/SingletonEnumV2;
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: ldc           #2                  // class design/SingletonEnumV2
         2: aload_0
         3: invokestatic  #12                 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
         6: checkcast     #2                  // class design/SingletonEnumV2
         9: areturn
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      10     0  name   Ljava/lang/String;
  //一个私有的构造函数,并且接受一个 String类型的参数 和Int类型的 参数
  private design.SingletonEnumV2();
    descriptor: (Ljava/lang/String;I)V
    flags: (0x0002) ACC_PRIVATE
    Code:
      stack=3, locals=3, args_size=3
         0: aload_0
         1: aload_1
         2: iload_2
         3: invokespecial #18                 // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
         6: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       7     0  this   Ldesign/SingletonEnumV2;
    Signature: #52                          // ()V

//枚举类中定义的方法
  public void doSomething();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #22                 // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #28                 // String doSomething
         5: invokevirtual #30                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 9: 0
        line 10: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  this   Ldesign/SingletonEnumV2;
//静态代码块:执行对象的实例化过程
  static {};
    descriptor: ()V
    flags: (0x0008) ACC_STATIC
    Code:
      stack=4, locals=0, args_size=0
         //new 一个SingletonEnumV2对象
         0: new           #2                  // class design/SingletonEnumV2
         //其作用就是复制之前分配的空间的引用,并压入栈顶
         3: dup
         //将常量INSTANCE 加载到栈顶
         4: ldc           #36                 // String INSTANCE
         6: iconst_0
         //执行构造方式
         7: invokespecial #38                 // Method "<init>":(Ljava/lang/String;I)V
         //给INSTANCE赋值:上一步通过构造方法创建的对象:SingletonEnumV2
        10: putstatic     #39                 // Field INSTANCE:Ldesign/SingletonEnumV2;
        //iconst_1 //将一个常量加载到操作数栈
        13: iconst_1
        //将SingletonEnumV2对象写入array数据
        14: anewarray     #2                  // class design/SingletonEnumV2
        //其作用就是复制之前分配的空间的引用,并压入栈顶
        17: dup
        18: iconst_0
        19: getstatic     #39                 // Field INSTANCE:Ldesign/SingletonEnumV2;
        //astore将此时的栈顶值弹出存入局部变量中去
        22: aastore
        23: putstatic     #1                  // Field $VALUES:[Ldesign/SingletonEnumV2;
        26: return
      LineNumberTable:
        line 5: 0
        line 3: 13
}
Signature: #54                          // Ljava/lang/Enum<Ldesign/SingletonEnumV2;>;
SourceFile: "SingletonEnumV2.java"

单例模式并不安全
《effective java》中只简单的提了几句话:“享有特权的客户端可以借助AccessibleObject.setAccessible方法,通过反射机制调用私有构造器。如果需要抵御这种攻击,可以修改构造器,让它在被要求创建第二个实例的时候抛出异常。

任何一个readObject方法,不管是显式的还是默认的,它都会返回一个新建的实例,这个新建的实例不同于该类初始化时创建的实例。”当然,这个问题也是可以解决的,想详细了解的同学可以翻看《effective java》第77条:对于实例控制,枚举类型优于readResolve()方法;**

总结:如下图:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

stay_running

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值