手撕单例模式(详解)

一:设计模式概述

1:设计模式的概念

软件设计模式(Software Design Pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它描述了在软件设计过程中的一些不断重复发生的问题,以及该问题的解决方案。也就是说,它是解决特定问题的一系列套路,是前辈们的代码设计经验的总结,具有一定的普遍性,可以反复使用。其目的是为了提高代码的可重用性、代码的可读性和代码的可靠性。(也就是给我们一种设计代码的模板 避免我们的代码错误太多)

2:学习设计模式的意义

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

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

3:软件设计模式的基本要素

这个也就是我们的设计模式的名称,应用场景(遇到的什么问题),解决方案,使用该设计模式后的效果

  • 每一个模式都有自己的名字,通常用一两个词来描述,可以根据模式的问题、特点、解决方案、功能和效果来命名。模式名称(PatternName)有助于我们理解和记忆该模式,也方便我们来讨论自己的设计。
  • 问题(Problem)描述了该模式的应用环境,即何时使用该模式。它解释了设计问题和问题存在的前因后果,以及必须满足的一系列先决条件。
  • 模式问题的解决方案(Solution)包括设计的组成成分、它们之间的相互关系及各自的职责和协作方式。因为模式就像一个模板,可应用于多种不同场合,所以解决方案并不描述一个特定而具体的设计或实现,而是提供设计问题的抽象描述和怎样用一个具有一般意义的元素组合(类或对象的 组合)来解决这个问题
  • 描述了模式的应用效果以及使用该模式应该权衡的问题,即模式的优缺点。主要是对时间和空间的衡量,以及该模式对系统的灵活性、扩充性、可移植性的影响,也考虑其实现问题。显式地列出这些效果(Consequence)对理解和评价这些模式有很大的帮助。

4:设计模式的类型

在这里插入图片描述

二:单例模式

1:单例模式是个什么鬼

  • 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

  • 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
    注意:

  • 1、单例类只能有一个实例。

  • 2、单例类必须自己创建自己的唯一实例。

  • 3、单例类必须给所有其他对象提供这一实例。
    关键词语: 创建型模式 自己创建实例,不能被实例化 单一对象,只能在堆当中有一个实例

2:单例模式的使用

  • **意图:**保证一个类仅有一个实例,并提供一个访问它的全局访问点。
  • **主要解决:**一个全局使用的类频繁地创建与销毁。
  • **何时使用:**当您想控制实例数目,节省系统资源的时候。
  • **如何解决:**判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
  • **关键代码:**构造函数是私有的。
  • 应用实例:
    • 1、一个班级只有一个班主任。
    • 2、Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。
    • 3、一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。
  • 优点:
    • 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
      (这里补充类加载,我们new 一个实例的时候 该类会被 加载;或则调用某个类的静态方法时候也会被加载,或者是该子类被加载的时候,父类也会 被加载)

    • 2、避免对资源的多重占用(比如写文件操作)。(无论谁去 调用 我们这个实例 该实例 在内存中只有一个)

  • **缺点:**没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
  • 使用场景
    • 1、要求生产唯一序列号。
    • 2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
    • 3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
      **注意事项:**getInstance() 方法中需要使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成 instance 被多次实例化。

3:单例实现过程

(1):创建单例类的步骤

  • 创建一个该单例类的对象(需要用static 修饰为静态属性 因为在下方的静态方法中只能用静态属性,而且下方的方法也必须是静态方法,因为静态方法被调用时,该类就会被类加载到jvm的内存中)
  • 让构造函数为私有的 这样该类 就不会被实例化
  • 对外提供唯一的可用对象

(2):代码

package com.wyj.singleton;
class SingleObject {//创建一个SingleObject对象// 用static 关键字修饰 表示该变量为静态变量  该类所有对象所共享的一个变量//下方static方法中 不能调用非Static属性private static SingleObject getInstance = new SingleObject();private String name;//让其构造函数为private,那么他不会被实例化private SingleObject (){}//获取唯一可用的对象// 这里用static关键字修饰的话, 当该静态方法被调用时该类就会被类加载public static SingleObject getSingleObject() {return getInstance;}void say() {System.out.println("hello");}public String getName() {return name;}public void setName(String name) {this.name = name;}
}
public class singleDemo {public static void main(String[] args) {// 我们并没有去new一个实例,而是调用了该实例的 自己建造实例的方法SingleObject object = SingleObject.getSingleObject();SingleObject object2 = SingleObject.getSingleObject();object.setName("wyj");object2.setName("www");System.out.println(object.getName());}
}

(3):验证每个引用变量共享一个实例

在这里插入图片描述

上方中我们创建了两个引用变量,我们同时操作同一个属性

4:几种单例实现的方式

(1):前言

  • lazy初始化:表示的是否延迟加载,它的核心思想是把对象的实例化延迟到真正调用该对象的时候,这样做的好处是可以减轻大量对象在实例化时对资源的小号,而不是在程序初始化的时候就预先将对象实例化。
    (也就是在堆中开辟空间是否延迟,因为一旦调用static 修饰的方法 那么该类就会被加载,无论懒汉式 还是饿汉式 单例类 他们均需被加载,那么这里的延迟加载 指的是在懒汉式中 我们只有调用到 getInstance() 才会在 堆空间 内为 该实例开辟 开辟空间;而饿汉式当中 只要当我们类加载初始化 就会在堆中开辟实例空间。)

(2):懒汉式,线程不安全

  • 懒汉式单例模式 只是声明对象,等到调用getInstanc()的时候才进行new 对象 因为他懒
  • **是否 Lazy 初始化:**是
  • **是否多线程安全:**否
  • **实现难度:**易
  • **描述:**这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。(当出现多个线程调用该方法时,会出现多个实例)
  • 这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。
class lazyUnsafe {private static lazyUnsafe lazyUnsafe;private lazyUnsafe(){//new 实例的时候会调用这个构造方法System.out.println(Thread.currentThread().getName());}public static lazyUnsafe getInstance() {if (lazyUnsafe == null) {//这里的线程不安全 就是发生在这里 如果 A线程执行到这里 正好被阻塞了//然后CPU的时间片切换到B线程 然后B线程 new 了一个实例 那么的话//当CPU再次切换到A线程的时候,那么的话就又会new了一个实例 这样就破坏了单实例//lazyUnsafe = new lazyUnsafe();}return lazyUnsafe;}public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Thread( ()->{lazyUnsafe.getInstance();//这里运行出几个线程  那就创造出几个实例 破坏了单例原则}                           // 在new lazyUnsafe()的时候  会调用空参构造方法,那么就会输出线程名).start();}
}  }

(3):懒汉式线程安全

  • **是否 Lazy 初始化:**是
  • **是否多线程安全:**是
  • **实现难度:**易
  • **描述:**这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。
    优点:第一次调用才初始化,避免内存浪费。

缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。

getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)。

  • 解释线程安全:
    虽然我们开启了10个线程,但是只有一个线程可以new LazyUnsafe() 调用构造方法输出线程名

      因为我们加锁了, 在一次运行当中,所有线程共享jvm中的堆区,也就是共享我们的
    

同一个在堆中new出的实例,那么后面的线程调用方法getInstance()的话,因为 lazyUnsafe != null 了 所以不会再 new 实例了

class lazysafe {private static lazySafe lazysafe;private lazySafe(){System.out.println(Thread.currentThread().getName());}public static synchronized lazySafe getInstance() {if (lazysafe == null) {lazysafe = new lazySafe();}return lazyUnsafe;}public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Thread( ()-> {lazySafe.getInstance();//虽然我们开启了10个线程//但是只有一个线程可以new LazyUnsafe() 调用构造方法输出线程名}).start();                  //同一个在堆中new出的实例,那么后面的线程调用方法getInstance()的话  
}                                   //因为 lazyUnsafe != null 了 所以不会再 new 实例了}}

(4):双检锁/双重效验锁(DCL)

  • **DK 版本:**JDK1.5 起
  • **是否 Lazy 初始化:**是
  • **是否多线程安全:**是
  • **实现难度:**较复杂
  • **描述:**这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
  • 创建一个SingleLazyDCL的对象时加入了 volatitle关键字
    • 解决了什么问题?
      我们在进行 new SingleLazyDCL() 时 这不是一个原子操作,有三个过程

       1. 为该实例分配内存空间2. 执行构造方法 初始化对象3. 栈中引用变量指向 堆中的实例
      

如果正常执行 1 2 3 那也没啥问题

但是这里会出现指令重排 也就是 会出现 1 3 2

        * 那么当A线程指令执行到3时还未执行2指令时* 这时候CPU切换到B线程来执行,那么在第一个 if()判断时 因为 此时对象引用不为空了,那么就会直接返回这个对象的引用,但是这个对象还未执行构造方法初始化,那就返回了个残缺不全的对象  会报错。
* 为什么加入这个关键字呢

那么 我们加入了这个关键字就可以防止指令重排。

  • 为什么是两层 if 判断
    • 第一个if语句,用来确认调用getInstance()时instance是否为空,如果不为空即已经创建,则直接返回,如果为空,那么就需要创建实例,于是进入synchronized同步块。

    • synchronized加类锁,确保同时只有一个线程能进入,进入以后进行第二次判断,是因为,
      对于首个拿锁者,它的时段instance肯定为null,那么进入new Singleton()对象创建,

    • 在首个拿锁者的创建对象期间,可能有其他线程同步调用getInstance(),那么它们也会通过if进入到同步块试图拿锁然后阻塞(因为这时首拿锁的线程还在创建对象期间)。

    • 这样的话,当首个拿锁者完成了对象创建,之后的线程都不会通过第一个if了,而这期间阻塞的线程开始唤醒,它们则需要靠第二个if语句来避免再次创建对象。

    • 以上就是双检索的实现思路,synchronized与第二个if即是用来保证线程安全与不产生第二个实例

//双层锁的机制
public class SingleLazyDCL {private volatile static SingleLazyDCL singleLazyDCL;private SingleLazyDCL (){System.out.println(Thread.currentThread().getName());}public static SingleLazyDCL getInstance() {if (singleLazyDCL == null) {synchronized (SingleLazyDCL.class) {if (singleLazyDCL == null) {singleLazyDCL = new SingleLazyDCL();}}}return singleLazyDCL;}public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Thread( ()->{SingleLazyDCL.getInstance();}).start();}}}

(5):静态内部类 (懒汉单例)

  • 这里的体现单例模式是在于我们用的final关键字,当final修饰引用类型的变量的话,则在其初始化后就不能然后其指向另一个对象,那么但我们多个线程去调用getInstance()方法时
    由于已经有一个线程是第一个SingleInner singleInner = new SingleInner(),那么的话

那么接下来再有线程去 SingleHolder.singleInner的话是不能进行的,因为final修饰的引用变量,再其初始化后,就不能再去指向另外一个对象(实例)

  • **是否 Lazy 初始化:**是
  • **是否多线程安全:**是
  • **实现难度:**一般
  
public class SingleInner {private SingleInner(){System.out.println(Thread.currentThread().getName());}//final修饰的对于一个final变量,// 如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;// 如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。private static class SingleHolder{private static final SingleInner singleInner = new SingleInner();}public static SingleInner getInstance() {return SingleHolder.singleInner;}public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Thread(()->{SingleInner.getInstance();}).start();}}

(6):禁止反射破坏的(懒汉单例)

a:第一次破坏和防御

1:先展示反射如何破坏

package com.wyj.singleton;import java.lang.reflect.Constructor;public class ReflectBreakSingle {private ReflectBreakSingle() {System.out.println(Thread.currentThread().getName());}private static volatile ReflectBreakSingle reflectBreakSingle;public static ReflectBreakSingle getInstance() {if (reflectBreakSingle == null) {synchronized (ReflectBreakSingle.class) {if (reflectBreakSingle == null) {reflectBreakSingle = new ReflectBreakSingle();}}}return reflectBreakSingle;}public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException {//正常情况下 调用两次getInstance()的话,我们产生两个引用变量,但是只有一个实例ReflectBreakSingle instance1 = getInstance();ReflectBreakSingle instance2 = getInstance();System.out.println("正常情况:" + (instance1 == instance2));//返回true 因为我们的引用变量地址相同//利用反射破坏//通过反射获取该类的Class对象Class<?> aClass = Class.forName("com.wyj.singleton.ReflectBreakSingle");//通过Class对象获取该类的 空参构造器Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(null);//爆破  破坏该类的空参的私有属性declaredConstructor.setAccessible(true);//通过反射获取该类实例 也就是相当于 new ReflectBreakSingle();ReflectBreakSingle instance3 = (ReflectBreakSingle) aClass.newInstance();//调用getInstance再来一次获取获取该类的实例ReflectBreakSingle instance4 = getInstance();//如果单例没有被破坏的话 那么这两个实例返回的是true,因为堆中只有一个实例,但是单例被破坏了的话就会产生两个实例//那么引用变量对应的地址的值是不相同的System.out.println("利用反射破坏" + (instance3 == instance4));}
}

2:懒汉单例进行防御
其实也就是在我们的 空参构造器中加入一个判断,如果已经建立过一次了就不允许再创建了

package com.wyj.singleton;import java.lang.reflect.Constructor;public class ReflectBreakSingle {private ReflectBreakSingle() {if(reflectBreakSingle != null) {throw new RuntimeException("不允许反射破坏");}}private static volatile ReflectBreakSingle reflectBreakSingle;public static ReflectBreakSingle getInstance() {if (reflectBreakSingle == null) {synchronized (ReflectBreakSingle.class) {if (reflectBreakSingle == null) {reflectBreakSingle = new ReflectBreakSingle();}}}return reflectBreakSingle;}public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException {//正常情况下 调用两次getInstance()的话,我们产生两个引用变量,但是只有一个实例ReflectBreakSingle instance1 = getInstance();ReflectBreakSingle instance2 = getInstance();System.out.println("正常情况:" + (instance1 == instance2));//返回true 因为我们的引用变量地址相同//利用反射破坏//通过反射获取该类的Class对象Class<?> aClass = Class.forName("com.wyj.singleton.ReflectBreakSingle");//通过Class对象获取该类的 空参构造器Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(null);//爆破  破坏该类的空参的私有属性declaredConstructor.setAccessible(true);//通过反射获取该类实例 也就是相当于 new ReflectBreakSingle();ReflectBreakSingle instance3 = (ReflectBreakSingle) aClass.newInstance();//调用getInstance再来一次获取获取该类的实例ReflectBreakSingle instance4 = getInstance();//如果单例没有被破坏的话 那么这两个实例返回的是true,因为堆中只有一个实例,但是单例被破坏了的话就会产生两个实例//那么引用变量对应的地址的值是不相同的System.out.println("利用反射破坏" + (instance3 == instance4));}
}

b:第二次破坏和防御

  • 破坏的话 我们是直接用反射创建两个实例 那么我们就没有调用 getInstance()方法了,这样的话 我们的单例中的静态变量 就没有被赋值了,所以那个构造函数中的判空 就不起作用了。
package com.wyj.singleton;import java.lang.reflect.Constructor;public class ReflectBreakSingle2 {private static volatile ReflectBreakSingle2 reflectBreakSingle;static {System.out.println("代码块:"+reflectBreakSingle);}private ReflectBreakSingle2() {
//        System.out.println("构造方法:"+reflectBreakSingle);if(reflectBreakSingle != null) {throw new RuntimeException("不允许反射破坏");}}public static ReflectBreakSingle2 getInstance() {if (reflectBreakSingle == null) {synchronized (ReflectBreakSingle2.class) {if (reflectBreakSingle == null) {reflectBreakSingle = new ReflectBreakSingle2();System.out.println("hello");}}}return reflectBreakSingle;}public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException {//正常情况下 调用两次getInstance()的话,我们产生两个引用变量,但是只有一个实例
//        ReflectBreakSingle instance1 = getInstance();
//        ReflectBreakSingle instance2 = getInstance();
//
//        System.out.println("正常情况:" + (instance1 == instance2));//返回true 因为我们的引用变量地址相同//利用反射破坏//通过反射获取该类的Class对象Class<?> aClass = Class.forName("com.wyj.singleton.ReflectBreakSingle2");//通过Class对象获取该类的 空参构造器Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(null);//爆破  破坏该类的空参的私有属性declaredConstructor.setAccessible(true);//通过反射获取该类实例 也就是相当于 new ReflectBreakSingle();ReflectBreakSingle2 instance3 = (ReflectBreakSingle2) aClass.newInstance();/*** 这样写的话就可以破坏单例模式,上方通过反射创建了一次实例,* 下方这里再通过getInstance()方法再创建一次实例,就破坏了单例模式,这里不会抛出异常* 因为第一创建实例的时候 我们并没有给*  private static volatile ReflectBreakSingle2 reflectBreakSingle;*  ReflectBreakSingle2 进行赋值,那么的话 我们在构造函数中的判空判断就失效了*/getInstance();}
}
  • 那么单例是如何防御的呢
    首先我们选择一个静态的标志位变量, 因为是静态变量 所以只有我们在第一次进行类加载的时候进行加载,那么我们通过在构造方法中给其修改变量的值,那么的话下一次再创建实例的时候,我们构造方法中的设置的判断条件就会起作用了
package com.wyj.singleton;import java.lang.reflect.Constructor;
import java.lang.reflect.Field;public class ReflectBreakSingle3 {private static volatile ReflectBreakSingle3 reflectBreakSingle;private static boolean flag = false;private ReflectBreakSingle3() {if ( flag == false) {flag = true;} else {throw new RuntimeException("禁止破坏单例");}}public static ReflectBreakSingle3 getInstance() {if (reflectBreakSingle == null) {synchronized (ReflectBreakSingle3.class) {if (reflectBreakSingle == null) {reflectBreakSingle = new ReflectBreakSingle3();System.out.println("hello");}}}return reflectBreakSingle;}public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, NoSuchFieldException {//利用反射破坏//通过反射获取该类的Class对象Class<?> aClass = Class.forName("com.wyj.singleton.ReflectBreakSingle3");//通过Class对象获取该类的 空参构造器Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(null);//爆破  破坏该类的空参的私有属性declaredConstructor.setAccessible(true);//通过反射获取该类实例 也就是相当于 new ReflectBreakSingle();ReflectBreakSingle3 instance3 = (ReflectBreakSingle3) aClass.newInstance();/*** 这样我们加入一个静态的标志位,那么在第二次new实例的时候由于 static 修饰的静态变量只会在第一次* new 实例的时候加载一次  那么我们在构造函数给其进行赋值的话  那么在第二次的new 实例的时候* 这个静态变量的值是不会变的*/getInstance();}
}

c:反射还可以再进行破坏

我们通过反射获取到我们的这个静态变量,然后的话,我们修改这个静态变量的值,这样就可以破坏,构造方法中的判断条件。

package com.wyj.singleton;import java.lang.reflect.Constructor;
import java.lang.reflect.Field;public class ReflectBreakSingle4 {private static volatile ReflectBreakSingle4 reflectBreakSingle;private static boolean flag = false;private ReflectBreakSingle4() {if ( flag == false) {flag = true;} else {throw new RuntimeException("禁止破坏单例");}}public static ReflectBreakSingle4 getInstance() {if (reflectBreakSingle == null) {synchronized (ReflectBreakSingle4.class) {if (reflectBreakSingle == null) {reflectBreakSingle = new ReflectBreakSingle4();System.out.println("hello");}}}return reflectBreakSingle;}public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, NoSuchFieldException {//利用反射破坏//通过反射获取该类的Class对象Class<?> aClass = Class.forName("com.wyj.singleton.ReflectBreakSingle4");//通过Class对象获取该类的 空参构造器Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(null);//爆破  破坏该类的空参的私有属性declaredConstructor.setAccessible(true);//通过反射获取该类实例 也就是相当于 new ReflectBreakSingle();ReflectBreakSingle4 instance3 = (ReflectBreakSingle4) aClass.newInstance();//我们通过反射获取到该字段在进行修改为falseField flag = ReflectBreakSingle4.class.getDeclaredField("flag");flag.set(instance3,false);/*** 这样我们加入一个静态的标志位,那么在第二次new实例的时候由于 static 修饰的静态变量只会在第一次* new 实例的时候加载一次  那么我们在构造函数给其进行赋值的话  那么在第二次的new 实例的时候* 这个静态变量的值是不会变的*/getInstance();}
}

那么这时候我们就应该知道的是 我们得找一个不允许反射破坏的单例 那就是枚举

(7)饿汉式

  • **是否 Lazy 初始化:**否(延迟初始化)
  • **是否多线程安全:**是
  • **实现难度:**易
  • **描述:**这种方式比较常用,但容易产生垃圾对象。
  • 优点:没有加锁,执行效率会提高。
  • 缺点:类加载时就初始化,浪费内存。
    它基于 classloader 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。
package com.wyj.singleton;public class SingleHungry {private static SingleHungry singleHungry = new SingleHungry();private SingleHungry(){System.out.println(Thread.currentThread().getName());}public static SingleHungry getInstance() {return singleHungry;}public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Thread( ()->{SingleHungry.getInstance();//这里不会输出构造器当中的方法了}).start();                   //因为调用该类的静态方法的时候,该类已经 n}                                //new 完实例了  那么我们就可以知道的是}                                   //那就没法再去 输出我们线程的名字    }

(8):枚举单例

  • 要注意的是 枚举当中空参构造器 是假的 我们通过反编译可以得到的是 里面的构造器是
    带有两个参数的(String 和 int )
package com.wyj.singleton;import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;public enum SingleEnum {Instance;public SingleEnum getInstance() {return Instance;}public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//        SingleEnum instance = SingleEnum.Instance;
//        SingleEnum instance2 = SingleEnum.Instance;
//
//        System.out.println(instance == instance2);Class<?> aClass = Class.forName("com.wyj.singleton.SingleEnum");//这里通过反编译的话:jad -s java SingleEnum.class// 我们得到了原来枚举其实不是空参构造器//真实的底层其实是://  private SingleEnum(String s, int i)//    {//        super(s, i);//    }Constructor<?> constructor = aClass.getDeclaredConstructor(String.class,int.class);Object instance = constructor.newInstance();SingleEnum instance1 = SingleEnum.Instance;}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/307555.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微软发布.NET 5.0 RC1,未来将只有一个.NET

本周早些时候&#xff0c;微软发布了.NET 5.0 RC1&#xff0c;这是.NET 5 在11 月正式发布之前的第一个“go-live”版本。.NET 5 是.NET 生态系统的统一平台&#xff0c;将所有组件打包成一个跨平台包。新版本包含了.NET Core 3 的很多改进&#xff0c;包括新的语言版本(C# 9 和…

深度学习入门笔记(4)—— Pytorch 和计算图的简单介绍

Pytorch 顾名思义&#xff0c;就是 Torch 7 移植到 Python 的版本&#xff0c;其最大亮点就是自动微分、动态计算图和 Numpy 集成。 Pytorch 相比于 Numpy&#xff0c;优点在于支持 GPU 计算甚至是多设备计算&#xff0c;以及动态计算图。 在 import 的时候&#xff0c;得记住 …

leetcode26. 删除有序数组中的重复项

一:题目 二:上码 // class Solution { // public: // int removeDuplicates(vector<int>& nums) { // //排序 // sort(nums.begin(),nums.end());// //调用unique&#xff08;&#xff09;将vector当中的相同元素 放到 容器的最后面 //…

楼继伟:现有5G技术很不成熟

日前&#xff0c;财政部原部长楼继伟表示&#xff0c;基础设施适度超前是必要的&#xff0c;但有些方面过度超前&#xff0c;抬高了用户成本或不可持续的公共部门债务。现有5G技术很不成熟&#xff0c;数千亿级的投资已经布下&#xff0c;而且运营成本极高&#xff0c;找不到应…

深度学习入门笔记(5)—— Pytorch API 的基本用法框架

第一步&#xff0c;通常是定义我们的神经网络模型。类名后的括号表示我们定义的类会继承 torch.nn.Module&#xff0c;而 super 函数就是调用父类的方法&#xff0c;后面跟 __init__() 就是调用 torch.nn.Module 的构造函数&#xff0c;然后就是我们自定义的模型及其参数。在 f…

跟我一起学.NetCore之路由的最佳实现

前言路由&#xff0c;这词绝对不陌生&#xff0c;不管在前端还是后端都经常提到&#xff0c;而这节不说其他&#xff0c;就聊.NetCore的路由&#xff1b;在之前的Asp.Net MVC 中&#xff0c;路由算是面试时必问的考点&#xff0c;可见其重要性&#xff0c;它的主要作用是映射UR…

leetcode844. 比较含退格的字符串

一:题目 二:上码 class Solution {public:/**思路:1.利用栈 我们将字符串中的单个元素都入栈 当遇到#的时候将将栈顶元素弹出*/bool backspaceCompare(string s, string t) {stack<int> s1;stack<int> s2;for (int i 0; i < s.size(); i) {if(s[i] ! #) s1.pu…

BeetleX之HTTP网关部署

BeetleX组件提供了HTTP网关组件&#xff0c;但使用过程中需要自己写宿主程序来启动它。为了更好的直接使用&#xff0c;组件发布了一个基于.net core 3.1的可运行版本程序并集成了管理插件&#xff1b;只需下载程序即可以在linux或windows下部署一个HTTP网关部署。部署安装运行…

深度学习入门笔记(6)—— Logistic Regression

对比第三节中的 Adaline 和 Logistic Regression&#xff0c;可以发现它们只有两点不同&#xff1a;1、激活函数&#xff0c;Adaline 中的激活函数是恒等函数&#xff08;线性&#xff09;&#xff0c;而 Logistic Regression 中的激活函数是 Sigmoid 函数&#xff08;非线性&a…

微服务模式下,实现前后端多资源服务调用

MicroServices首先&#xff0c;我先解释下&#xff0c;文章标题的意思&#xff1a;咋看起来特别像是一个标题党????&#xff0c;可能是我没想好怎么表达&#xff0c;其实白话文就是&#xff1a;在微服务场景下&#xff0c;肯定会有很多子服务API&#xff0c;那多个前端项目…

leetcode54. 螺旋矩阵(详解)

一:题目 二:上码 class Solution { public:/**思路:1.1 2 3 45 6 7 89 10 11 1213 14 15 162.我们模拟顺时针的顺序,那么得分为4步 上面得一行 (1,2,3)左面得一行 (4,8,12)下面得一行 (16,15,14)右面得一行 (13,9,5)可以看到我们在取数字得时候,我们是每次只取…

深度学习入门笔记(7)—— Multinomial Logistic Regression / Softmax Regression

首先介绍一个非常著名的多分类数据集 MNIST&#xff0c;也就是 0 到 9 的手写数字数据集。每个图像都是 28 * 28&#xff0c;用于Pytorch 数据读取的格式是 NCHW&#xff0c;即 Number、Channel、Height、Weight。 读取图像之后&#xff0c;就能看到一个只有单通道的&#xff0…

[C#.NET 拾遗补漏]09:数据标注与数据校验

数据标注&#xff08;Data Annotation&#xff09;是类或类成员添加上下文信息的一种方式&#xff0c;在 C# 通常用特性&#xff08;Attribute&#xff09;类来描述。它的用途主要可以分为下面这三类&#xff1a;验证 Validation&#xff1a;向数据添加验证规则展现 Display&am…

2023届春招实习拉钩一面凉经

面完就当挂了 老想着其实自己其实还能回答的更好 可回不去了 计网的的ioc七层模型各层的作用 为什么是七层模型而不是两层模型 SpringBoot最核心的注解是什么 Spring中IOC原理以及相关注解 说说对Mysql数据库索引的了解 map有哪些实现 分别讲讲&#xff1f; 项目中数据库…

Pytorch中损失函数 NLLLOSS 和 CROSSENTROPYLOSS 的区别

NLLLOSS 是 negative log likelihood loss 负对数似然损失函数&#xff0c;CROSSENTROPYLOSS 是交叉熵损失函数&#xff0c;它们都是用于多分类任务的损失函数&#xff0c;正如我在笔记中提到的&#xff0c;它们实际上是一样的&#xff0c;在 Pytorch 中这两者的区别就只在于接…

收好这张MySQL导图,全是知识点!

感谢阅读&#xff0c;直接上干货&#xff01;最新整理的MySQL知识点全图&#xff0c;完整系统超3万字。直接跳转文末&#xff0c;下载源文件&#xff01;开源跨平台的.NET Core越来越热门了&#xff0c;腾讯、网易、顺丰等大厂都在招聘&#xff0c;而且都开始明确要求MySQL了&a…

深度学习入门笔记(8)—— Multilayer Perceptron 多层感知器

所谓多层感知器&#xff0c;其实就是具有一个或多个隐藏层&#xff08;hidden layer&#xff09;的全连接前馈神经网络&#xff0c;如图所示&#xff0c;在最重要的求取损失关于权重的偏导数的过程上&#xff0c;跟之前的 Logistic Regression 和 Softmax Regression 一样&…

蓝桥杯-填空题-门牌制作

一&#xff1a;题目 二&#xff1a;代码 #include <iostream> using namespace std; int main() {int nums 2020;int count 0;string str to_string(nums);for(int i 1; i < nums; i) {string str to_string(i);for(auto temp: str) {if(temp 2){count;}}}cout…

.NET Core中间件与依赖注入的一些思考

点击上方蓝字"小黑在哪里"关注我吧1.起源? 为什么会有这篇文章呢? 源于我看了老A的aspnet core 3 框架揭秘[1] 请求管道 篇产生的疑惑?三点疑惑:Singleton服务中注入Scoped服务产生内存泄露?关于中间件的生命周期是Singleton的?怎么避免中间件、Singleton服务中…

深度学习入门笔记(9)—— Regularization to avoid overfitting 用正则化来避免过拟合

在维基百科的词条中&#xff0c;正则化就是让答案变得更简单的&#xff0c;目的是防止过拟合。在分类上可以分为显式正则化&#xff08;对目标函数添加先验、惩罚、约束、成本&#xff09;和隐式正则化&#xff08;所有其他形式的正则化如提前停止、集成学习、Dropout 等&#…