• java单例模式--懒汉式、饿汉式(第二次学习)


    目录

    java单例模式--懒汉式、饿汉式

    1、要点

    2、单例模式的类型(五种)

    2.1 饿汉式

    2.2 枚举饿汉式(推荐使用)

    2.3 懒汉式单例

    2.4 DCL懒汉式(双检锁的懒汉式)

    2.5 内部类懒汉式(推荐使用)

    3、jdk中的体现

    1、饿汉式的单例模式

    2、枚举饿汉式

    3、懒汉式的DCL(双检锁)

    4、内部类懒汉式


    java单例模式--懒汉式、饿汉式

    单例模式,属于创建类型的一种常用的软件设计模式。(23种设计模式之一)通过单例模式的方法创建的类在当前进程中只有一个实例

    1、要点

    • 构造器私有化

    • 定义静态变量存储这个对象唯一实例

    • 直接暴露或者封装使用getter方法提供实例的获取

    2、单例模式的类型(五种)

    饿汉式、枚举饿汉式、懒汉式、双锁懒汉式、内部类懒汉式 五种

    2.1 饿汉式

    饿汉式:即当类初始化的时候就创建实例对象 --就叫做饿汉式

    代码

    1. package com.sofwin.singletonDemo;
    2. /**
    3. * @packageName: com.sofwin.singletonDemo
    4. * @author: wentao
    5. * @date: 2022/11/21 10:00
    6. * @version: 1.0
    7. * @email 1660420659@qq.com
    8. * @description: 单例模式-饿汉式
    9. *
    10. * 要点:
    11. *     1.构造器私有化
    12. *     2.定义静态常量存储对象唯一实例
    13. *     3.通过getter方法获取实例对象
    14. */
    15. public class Singleton01 {
    16.    /**
    17.     * 构造器私有化
    18.     */
    19.    private static final Singleton01 SINGLETON_01 = new Singleton01();
    20.    private  Singleton01() {
    21.        System.out.println("private Singleton01");
    22.   }
    23.    public static Singleton01 getSingleton01() {
    24.        return SINGLETON_01;
    25.   }
    26.    /**
    27.     * 通过调用静态方法,实现类的初始化
    28.     * 判断实例对象是类初始化就创建了 还是调用获取实例对象的时候才进行创建
    29.     */
    30.    public static void otherMethod() {
    31.        System.out.println("判断是懒汉式还是饿汉式");
    32.   }
    33. }

    测试

    1. package com.sofwin.singletonDemo;
    2. import org.junit.Test;
    3. /**
    4. * @packageName: com.sofwin.singletonDemo
    5. * @author: wentao
    6. * @date: 2022/11/21 10:09
    7. * @version: 1.0
    8. * @email 1660420659@qq.com
    9. * @description: 单例模式的测试
    10. */
    11. public class SingletonTest {
    12.    //单例模式-饿汉式
    13.    @Test
    14.    public void SinletonTest01() {
    15.        //先判断是否类初始化的时候就创建对象了
    16.        Singleton01.otherMethod();
    17.        System.out.println("=================");
    18.        Singleton01 singleton01 = Singleton01.getSingleton01();
    19.        Singleton01 singleton02 = Singleton01.getSingleton01();
    20.        System.out.println(singleton01);
    21.        System.out.println(singleton02);
    22.   }
    23. }

    (类加载就创建实例---饿汉式) 

    这种设计有三种方法可以进行破坏

    1、反射破坏单例

    1. package com.sofwin.singletonDemo;
    2. import org.junit.Test;
    3. import java.lang.reflect.Constructor;
    4. import java.lang.reflect.InvocationTargetException;
    5. /**
    6. * @packageName: com.sofwin.singletonDemo
    7. * @author: wentao
    8. * @date: 2022/11/21 10:09
    9. * @version: 1.0
    10. * @email 1660420659@qq.com
    11. * @description: 单例模式的测试
    12. */
    13. public class SingletonTest {
    14.    //单例模式-饿汉式
    15.    @Test
    16.    public void SinletonTest01() throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
    17.        //先判断是否类初始化的时候就创建对象了
    18.        Singleton01.otherMethod();
    19.        System.out.println("=================");
    20.        Singleton01 singleton01 = Singleton01.getSingleton01();
    21.        Singleton01 singleton02 = Singleton01.getSingleton01();
    22.        System.out.println(singleton01);
    23.        System.out.println(singleton02);
    24.        System.out.println("暴力破解");
    25.        SingletonTest.reflection(Singleton01.class);
    26.   }
    27.    /**
    28.     * 反射破坏单例pos
    29.     * @param clazz 类对象
    30.     */
    31.    public static void reflection(Class clazz) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    32.        //获取无参构造方法
    33.        Constructor declaredConstructor = clazz.getDeclaredConstructor();
    34.        //暴力破解
    35.        declaredConstructor.setAccessible(true);
    36.        //通过反射创建实例对象
    37.        Object o = declaredConstructor.newInstance();
    38.        System.out.println(o);
    39.   }
    40. }
    ​
    通过反射暴力破解,得到新的实例对象 

    改进

    1. package com.sofwin.singletonDemo;
    2. /**
    3. * @packageName: com.sofwin.singletonDemo
    4. * @author: wentao
    5. * @date: 2022/11/21 10:00
    6. * @version: 1.0
    7. * @email 1660420659@qq.com
    8. * @description: 单例模式-饿汉式
    9. *
    10. * 要点:
    11. *     1.构造器私有化
    12. *     2.定义静态常量存储对象唯一实例
    13. *     3.通过getter方法获取实例对象
    14. */
    15. public class Singleton01 {
    16.    /**
    17.     * 构造器私有化
    18.     */
    19.    private static final Singleton01 SINGLETON_01 = new Singleton01();
    20.    private  Singleton01() {
    21.        //防止反射破坏单例
    22.        if (SINGLETON_01 !=null) {
    23.          throw new RuntimeException("单例对象不能重复创建");
    24.       }
    25.        System.out.println("private Singleton01");
    26.   }
    27.    public static Singleton01 getSingleton01() {
    28.        return SINGLETON_01;
    29.   }
    30.    /**
    31.     * 通过调用静态方法,实现类的初始化
    32.     * 判断实例对象是类初始化就创建了 还是调用获取实例对象的时候才进行创建
    33.     */
    34.    public static void otherMethod() {
    35.        System.out.println("判断是懒汉式还是饿汉式");
    36.   }
    37. }
     

    2、反序列化破坏单例

    Singleton01类要实现Serializable

    1. package com.sofwin.singletonDemo;
    2. import java.io.Serializable;
    3. /**
    4. * @packageName: com.sofwin.singletonDemo
    5. * @author: wentao
    6. * @date: 2022/11/21 10:00
    7. * @version: 1.0
    8. * @email 1660420659@qq.com
    9. * @description: 单例模式-饿汉式
    10. *
    11. * 要点:
    12. *     1.构造器私有化
    13. *     2.定义静态常量存储对象唯一实例
    14. *     3.通过getter方法获取实例对象
    15. */
    16. public class Singleton01 implements Serializable {
    17.    /**
    18.     * 构造器私有化
    19.     */
    20.    private static final Singleton01 SINGLETON_01 = new Singleton01();
    21.    private  Singleton01() {
    22.        //防止反射破坏单例
    23.        if (SINGLETON_01 !=null) {
    24.          throw new RuntimeException("单例对象不能重复创建");
    25.       }
    26.        System.out.println("private Singleton01");
    27.   }
    28.    public static Singleton01 getSingleton01() {
    29.        return SINGLETON_01;
    30.   }
    31.    /**
    32.     * 通过调用静态方法,实现类的初始化
    33.     * 判断实例对象是类初始化就创建了 还是调用获取实例对象的时候才进行创建
    34.     */
    35.    public static void otherMethod() {
    36.        System.out.println("判断是懒汉式还是饿汉式");
    37.   }
    38. }

    测试代码

    1. package com.sofwin.singletonDemo;
    2. import org.junit.Test;
    3. import java.io.*;
    4. import java.lang.reflect.Constructor;
    5. import java.lang.reflect.InvocationTargetException;
    6. /**
    7. * @packageName: com.sofwin.singletonDemo
    8. * @author: wentao
    9. * @date: 2022/11/21 10:09
    10. * @version: 1.0
    11. * @email 1660420659@qq.com
    12. * @description: 单例模式的测试
    13. */
    14. public class SingletonTest {
    15.    //单例模式-饿汉式
    16.    @Test
    17.    public void SinletonTest01() throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, IOException, ClassNotFoundException {
    18.        //先判断是否类初始化的时候就创建对象了
    19.        Singleton01.otherMethod();
    20.        System.out.println("=================");
    21.        Singleton01 singleton01 = Singleton01.getSingleton01();
    22.        Singleton01 singleton02 = Singleton01.getSingleton01();
    23.        System.out.println(singleton01);
    24.        System.out.println(singleton02);
    25. //       System.out.println("反射暴力破解");
    26. //       SingletonTest.reflection(Singleton01.class);
    27.        System.out.println("反序列化破解");
    28.        SingletonTest.serialzable(singleton02);
    29.   }
    30.    /**
    31.     * 反射破坏单例pos
    32.     * @param clazz 类对象
    33.     */
    34.    public static void reflection(Class clazz) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    35.        //获取无参构造方法
    36.        Constructor declaredConstructor = clazz.getDeclaredConstructor();
    37.        //暴力破解
    38.        declaredConstructor.setAccessible(true);
    39.        //通过反射创建实例对象
    40.        Object o = declaredConstructor.newInstance();
    41.        System.out.println(o);
    42.   }
    43.    /**
    44.     * 反序列破解单例模式
    45.     * @param instance 对象
    46.     */
    47.    public static void serialzable(Object instance) throws IOException, ClassNotFoundException {
    48.        //将对象读取到baos中
    49.        ByteArrayOutputStream baos = new ByteArrayOutputStream();
    50.        ObjectOutputStream oos = new ObjectOutputStream(baos);
    51.        oos.writeObject(instance);
    52.        //通过字节数组获取对象
    53.        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
    54.        Object o = ois.readObject();
    55.        System.out.println(o);
    56.   }
    57. }

    改进

    1. package com.sofwin.singletonDemo;
    2. import java.io.Serializable;
    3. /**
    4. * @packageName: com.sofwin.singletonDemo
    5. * @author: wentao
    6. * @date: 2022/11/21 10:00
    7. * @version: 1.0
    8. * @email 1660420659@qq.com
    9. * @description: 单例模式-饿汉式
    10. *
    11. * 要点:
    12. *     1.构造器私有化
    13. *     2.定义静态常量存储对象唯一实例
    14. *     3.通过getter方法获取实例对象
    15. */
    16. public class Singleton01 implements Serializable {
    17.    /**
    18.     * 构造器私有化
    19.     */
    20.    private static final Singleton01 SINGLETON_01 = new Singleton01();
    21.    private  Singleton01() {
    22.        //防止反射破坏单例
    23.        if (SINGLETON_01 !=null) {
    24.          throw new RuntimeException("单例对象不能重复创建");
    25.       }
    26.        System.out.println("private Singleton01");
    27.   }
    28.    public static Singleton01 getSingleton01() {
    29.        return SINGLETON_01;
    30.   }
    31.    /**
    32.     * 通过调用静态方法,实现类的初始化
    33.     * 判断实例对象是类初始化就创建了 还是调用获取实例对象的时候才进行创建
    34.     */
    35.    public static void otherMethod() {
    36.        System.out.println("判断是懒汉式还是饿汉式");
    37.   }
    38.    /**
    39.     * 解决反序列化
    40.     * @return 实例对象
    41.     *
    42.     */
    43.    public Object readResolve() {
    44.        return SINGLETON_01;
    45.   }
    46. }
    ​ 

    这里 readResolve() 方法的解释

    简而言之,当我们通过反序列化readObejct方法获取对象的时候,会去寻找readResolve()方法,如果该方法不存在则直接返回新对象,如果该方法存在则按照该方法的内容返回对象,以确保如果我们之前实例化了单例对象,就返回该对象。

    为什么要使用功能readResolve()方法就解决了?

      

    这里看一下重点代码,readOrdinaryObject方法的代码片段:

    isInstantiable:如果一个serializable/externalizable的类可以在运行时被实例化,那么该方法就返回true。针对serializable和externalizable我会在其他文章中介绍。

    desc.newInstance:该方法通过反射的方式调用无参构造方法新建一个对象。

    然后关键是下面的代码

    1. if (obj != null &&
    2.    handles.lookupException(passHandle) == null &&
    3.    desc.hasReadResolveMethod())
    4. {
    5.    Object rep = desc.invokeReadResolve(obj);
    6.    if (unshared && rep.getClass().isArray()) {
    7.        rep = cloneArray(rep);
    8.   }
    9.    if (rep != obj) {
    10.        // Filter the replacement object
    11.        if (rep != null) {
    12.            if (rep.getClass().isArray()) {
    13.                filterCheck(rep.getClass(), Array.getLength(rep));
    14.           } else {
    15.                filterCheck(rep.getClass(), -1);
    16.           }
    17.       }
    18.        handles.setObject(passHandle, obj = rep);
    19.   }
    20. }

    hasReadResolveMethod:如果实现了serializable 或者 externalizable接口的类中包含readResolve则返回true

    invokeReadResolve:通过反射的方式调用要被反序列化的类的readResolve方法。

    所以,原理也就清楚了,主要在Singleton中定义readResolve方法,并在该方法中指定要返回的对象的生成策略,就可以防止单例被破坏。

    3、Unsafe破坏单例

    1. package com.sofwin.singletonDemo;
    2. import org.junit.Test;
    3. import org.springframework.objenesis.instantiator.util.UnsafeUtils;
    4. import java.io.*;
    5. import java.lang.reflect.Constructor;
    6. import java.lang.reflect.InvocationTargetException;
    7. /**
    8. * @packageName: com.sofwin.singletonDemo
    9. * @author: wentao
    10. * @date: 2022/11/21 10:09
    11. * @version: 1.0
    12. * @email 1660420659@qq.com
    13. * @description: 单例模式的测试
    14. */
    15. public class SingletonTest {
    16.    //单例模式-饿汉式
    17.    @Test
    18.    public void SinletonTest01() throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, IOException, ClassNotFoundException {
    19.        //先判断是否类初始化的时候就创建对象了
    20.        Singleton01.otherMethod();
    21.        System.out.println("=================");
    22.        Singleton01 singleton01 = Singleton01.getSingleton01();
    23.        Singleton01 singleton02 = Singleton01.getSingleton01();
    24.        System.out.println(singleton01);
    25.        System.out.println(singleton02);
    26. //       System.out.println("反射暴力破解");
    27. //       SingletonTest.reflection(Singleton01.class);
    28. //       System.out.println("反序列化破解");
    29. //       SingletonTest.serialzable(singleton02);
    30.        System.out.println("unsafe");
    31.        unsafe(singleton01.getClass());
    32.   }
    33.    /**
    34.     * 反射破坏单例pos
    35.     * @param clazz 类对象
    36.     */
    37.    public static void reflection(Class clazz) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    38.        //获取无参构造方法
    39.        Constructor declaredConstructor = clazz.getDeclaredConstructor();
    40.        //暴力破解
    41.        declaredConstructor.setAccessible(true);
    42.        //通过反射创建实例对象
    43.        Object o = declaredConstructor.newInstance();
    44.        System.out.println(o);
    45.   }
    46.    /**
    47.     * 反序列破解单例模式
    48.     * @param instance 对象
    49.     */
    50.    public static void serialzable(Object instance) throws IOException, ClassNotFoundException {
    51.        //将对象读取到baos中
    52.        ByteArrayOutputStream baos = new ByteArrayOutputStream();
    53.        ObjectOutputStream oos = new ObjectOutputStream(baos);
    54.        oos.writeObject(instance);
    55.        //通过字节数组获取对象
    56.        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
    57.        Object o = ois.readObject();
    58.        System.out.println(o);
    59.   }
    60.    /**
    61.     * 使用spring的工具类在jdk底层来访问,无序调用构造方法
    62.     * @param clazz
    63.     * @throws InstantiationException
    64.     */
    65.    public static void unsafe(Class clazz) throws InstantiationException {
    66.        Object o = UnsafeUtils.getUnsafe().allocateInstance(clazz);
    67.        System.out.println(o);
    68.   }
    69. }

    这个是无法解决的

    2.2 枚举饿汉式(推荐使用)

    代码

    1. package com.sofwin.singletonDemo;
    2. import sun.dc.pr.PRError;
    3. /**
    4. * @packageName: com.sofwin.singletonDemo
    5. * @author: wentao
    6. * @date: 2022/11/21 11:34
    7. * @version: 1.0
    8. * @email 1660420659@qq.com
    9. * @description: 单例模式 --枚举饿汉式
    10. */
    11. public enum Singleton02 {
    12.     INSTANCE;
    13.     private Singleton02() {
    14.         System.out.println("private Singleton02");
    15.     }
    16.     public static Singleton02 getInstance() {
    17.         return  INSTANCE;
    18.     }
    19.    /**
    20.     * 通过调用静态方法,实现类的初始化
    21.     * 判断实例对象是类初始化就创建了 还是调用获取实例对象的时候才进行创建
    22.     */
    23.    public static void otherMethod() {
    24.        System.out.println("判断是懒汉式还是饿汉式");
    25.   }
    26.    @Override
    27.    public String toString() {
    28.        //Integer.toHexString 转换为16进制
    29.        return getClass().getName()+"@"+Integer.toHexString(hashCode());
    30.   }
    31. }

    测试

    1. package com.sofwin.singletonDemo;
    2. import org.junit.Test;
    3. import org.springframework.objenesis.instantiator.util.UnsafeUtils;
    4. import java.io.*;
    5. import java.lang.reflect.Constructor;
    6. import java.lang.reflect.InvocationTargetException;
    7. /**
    8. * @packageName: com.sofwin.singletonDemo
    9. * @author: wentao
    10. * @date: 2022/11/21 10:09
    11. * @version: 1.0
    12. * @email 1660420659@qq.com
    13. * @description: 单例模式的测试
    14. */
    15. public class SingletonTest {
    16.    //单例模式-饿汉式
    17.    @Test
    18.    public void SinletonTest01() throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, IOException, ClassNotFoundException {
    19.        //先判断是否类初始化的时候就创建对象了
    20.        Singleton01.otherMethod();
    21.        System.out.println("=================");
    22.        Singleton02 singleton01 = Singleton02.INSTANCE;
    23.        Singleton02 singleton02 = Singleton02.INSTANCE;
    24.        System.out.println(singleton01);
    25.        System.out.println(singleton02);
    26.        //1、反射不存在,枚举没有无参构造器
    27. //       System.out.println("反射暴力破解");
    28. //       SingletonTest.reflection(Singleton02.class);
    29.        //2、反序列化不存在,readObject的时候会进行判断 ,如果是枚举类有特殊处理
    30. //       System.out.println("反序列化破解");
    31. //       SingletonTest.serialzable(singleton02);
    32.        //3、jdk这个是无法避免的,是可以破坏的
    33. //       System.out.println("unsafe");
    34. //       unsafe(singleton01.getClass());
    35.   }
    36.    /**
    37.     * 反射破坏单例pos
    38.     * @param clazz 类对象
    39.     */
    40.    public static void reflection(Class clazz) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    41.        //获取无参构造方法
    42.        Constructor declaredConstructor = clazz.getDeclaredConstructor();
    43.        //暴力破解
    44.        declaredConstructor.setAccessible(true);
    45.        //通过反射创建实例对象
    46.        Object o = declaredConstructor.newInstance();
    47.        System.out.println(o);
    48.   }
    49.    /**
    50.     * 反序列破解单例模式
    51.     * @param instance 对象
    52.     */
    53.    public static void serialzable(Object instance) throws IOException, ClassNotFoundException {
    54.        //将对象读取到baos中
    55.        ByteArrayOutputStream baos = new ByteArrayOutputStream();
    56.        ObjectOutputStream oos = new ObjectOutputStream(baos);
    57.        oos.writeObject(instance);
    58.        //通过字节数组获取对象
    59.        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
    60.        Object o = ois.readObject();
    61.        System.out.println(o);
    62.   }
    63.    /**
    64.     * 使用spring的工具类在jdk底层来访问,无序调用构造方法
    65.     * @param clazz
    66.     * @throws InstantiationException
    67.     */
    68.    public static void unsafe(Class clazz) throws InstantiationException {
    69.        Object o = UnsafeUtils.getUnsafe().allocateInstance(clazz);
    70.        System.out.println(o);
    71.   }
    72. }

    1、反射无法破坏枚举类单例

    枚举没有无参构造

    修改成有参构造

    1. /**
    2. * 反射破坏单例pos
    3. * @param clazz 类对象
    4. */
    5. public static void reflection(Class clazz) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    6.    //获取无参构造方法
    7.    Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class,int.class);
    8.    //暴力破解
    9.    declaredConstructor.setAccessible(true);
    10.    //通过反射创建实例对象
    11.    Object o = declaredConstructor.newInstance("OTHER",1);
    12.    System.out.println(o);
    13. }

    2、反序列话也无法破坏枚举类的单例

     3、Unsafe是可以破坏的

    2.3 懒汉式单例

    代码

    1. package com.sofwin.singletonDemo;
    2. import java.io.Serializable;
    3. /**
    4. * @packageName: com.sofwin.singletonDemo
    5. * @author: wentao
    6. * @date: 2022/11/21 10:00
    7. * @version: 1.0
    8. * @email 1660420659@qq.com
    9. * @description: 单例模式-懒汉式
    10. *
    11. * 要点:
    12. *     1.构造器私有化
    13. *     2.定义静态常量存储对象唯一实例
    14. *     3.通过getter方法获取实例对象
    15. */
    16. public class Singleton03 implements Serializable {
    17.    /**
    18.     * 构造器私有化
    19.     */
    20.    private static  Singleton03 instance = null;
    21.    private Singleton03() {
    22.        //防止反射破坏单例
    23.        if (instance !=null) {
    24.          throw new RuntimeException("单例对象不能重复创建");
    25.       }
    26.        System.out.println("private Singleton01");
    27.   }
    28.    public static Singleton03 getSingleton01() {
    29.        if (instance == null) {
    30.            instance = new Singleton03();
    31.       }
    32.        return instance;
    33.   }
    34.    /**
    35.     * 通过调用静态方法,实现类的初始化
    36.     * 判断实例对象是类初始化就创建了 还是调用获取实例对象的时候才进行创建
    37.     */
    38.    public static void otherMethod() {
    39.        System.out.println("判断是懒汉式还是饿汉式");
    40.   }
    41.    /**
    42.     * 解决反序列化
    43.     * @return 实例对象
    44.     *
    45.     */
    46.    public Object readResolve() {
    47.        return instance;
    48.   }
    49. }

    测试

    1. package com.sofwin.singletonDemo;
    2. import org.junit.Test;
    3. import org.springframework.objenesis.instantiator.util.UnsafeUtils;
    4. import java.io.*;
    5. import java.lang.reflect.Constructor;
    6. import java.lang.reflect.InvocationTargetException;
    7. /**
    8. * @packageName: com.sofwin.singletonDemo
    9. * @author: wentao
    10. * @date: 2022/11/21 10:09
    11. * @version: 1.0
    12. * @email 1660420659@qq.com
    13. * @description: 单例模式的测试
    14. */
    15. public class SingletonTest {
    16.    //单例模式-饿汉式
    17.    @Test
    18.    public void SinletonTest01() throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, IOException, ClassNotFoundException {
    19.        //先判断是否类初始化的时候就创建对象了
    20.        Singleton03.otherMethod();
    21.        System.out.println("=================");
    22.        Singleton03 singleton01 = Singleton03.getSingleton01();
    23.        Singleton03 singleton02 = Singleton03.getSingleton01();
    24.        System.out.println(singleton01);
    25.        System.out.println(singleton02);
    26.        //1、反射可以破坏单例
    27. //       System.out.println("反射暴力破解");
    28. //       SingletonTest.reflection(Singleton03.class);
    29.        //2、反序列化也可以破坏
    30. //       System.out.println("反序列化破解");
    31. //       SingletonTest.serialzable(singleton02);
    32.        //3、jdk这个是无法避免的,是可以破坏的
    33. //       System.out.println("unsafe");
    34. //       unsafe(singleton01.getClass());
    35.   }
    36.    /**
    37.     * 反射破坏单例pos
    38.     * @param clazz 类对象
    39.     */
    40.    public static void reflection(Class clazz) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    41.        //获取无参构造方法
    42.        Constructor declaredConstructor = clazz.getDeclaredConstructor();
    43.        //暴力破解
    44.        declaredConstructor.setAccessible(true);
    45.        //通过反射创建实例对象
    46.        Object o = declaredConstructor.newInstance();
    47.        System.out.println(o);
    48.   }
    49.    /**
    50.     * 反序列破解单例模式
    51.     * @param instance 对象
    52.     */
    53.    public static void serialzable(Object instance) throws IOException, ClassNotFoundException {
    54.        //将对象读取到baos中
    55.        ByteArrayOutputStream baos = new ByteArrayOutputStream();
    56.        ObjectOutputStream oos = new ObjectOutputStream(baos);
    57.        oos.writeObject(instance);
    58.        //通过字节数组获取对象
    59.        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
    60.        Object o = ois.readObject();
    61.        System.out.println(o);
    62.   }
    63.    /**
    64.     * 使用spring的工具类在jdk底层来访问,无序调用构造方法
    65.     * @param clazz
    66.     * @throws InstantiationException
    67.     */
    68.    public static void unsafe(Class clazz) throws InstantiationException {
    69.        Object o = UnsafeUtils.getUnsafe().allocateInstance(clazz);
    70.        System.out.println(o);
    71.   }
    72. }

    反射、序列化和unsafe都可以破坏

    但是这样都会导致一个问题多线程的情况会出现错误情况 (之前,懒汉式的常量和静态代码块中的代码,其实是jvm底层维护的是线程安全的,无序考虑) 因为这个是方法中创建实例就会出现线程安全的问题

    测试代码

    1. public class SingletonTest {
    2.    public static void main(String[]args){
    3.       new Thread( new Runnable() {
    4.            @Override
    5.            public void run() {
    6.                Singleton03 singleton01 = Singleton03.getSingleton01();
    7.                System.out.println(singleton01);
    8.           }
    9.       }).start();
    10.        new Thread( new Runnable() {
    11.            @Override
    12.            public void run() {
    13.                Singleton03 singleton01 = Singleton03.getSingleton01();
    14.                System.out.println(singleton01);
    15.           }
    16.       }).start();
    17.   }
    18. }

    正常应该是这种情况:

     但是多运行几次,也会出现这样的情况:

     造成的这种情况的结果是问题是:

    这个时候就出现了DCL懒汉式(双检锁的懒汉式)

    2.4 DCL懒汉式(双检锁的懒汉式)

    1. package com.sofwin.singletonDemo;
    2. import java.io.Serializable;
    3. /**
    4. * @packageName: com.sofwin.singletonDemo
    5. * @author: wentao
    6. * @date: 2022/11/21 10:00
    7. * @version: 1.0
    8. * @email 1660420659@qq.com
    9. * @description: 单例模式-懒汉式
    10. *
    11. * 要点:
    12. *     1.构造器私有化
    13. *     2.定义静态常量存储对象唯一实例
    14. *     3.通过getter方法获取实例对象
    15. */
    16. public class Singleton04 implements Serializable {
    17.    /**
    18.     * 构造器私有化
    19.     */
    20.    //这里加入valatile是保证这个变量是有序的,可见的
    21.    //防止指令重排序,在赋值语句后加入一个内存屏障,防止之前的赋值操作,进行重新排序
    22.    private static volatile Singleton04 instance = null;
    23.    private Singleton04() {
    24.        //防止反射破坏单例
    25.        if (instance !=null) {
    26.          throw new RuntimeException("单例对象不能重复创建");
    27.       }
    28.        System.out.println("private Singleton01");
    29.   }
    30.    public static Singleton04 getSingleton01() {
    31.        
    32.        if (instance == null) {
    33.            synchronized (Singleton04.class){
    34.                if (instance == null) {
    35.                    instance = new Singleton04();
    36.               }
    37.           }
    38.       }
    39.        return instance;
    40.   }
    41.    /**
    42.     * 通过调用静态方法,实现类的初始化
    43.     * 判断实例对象是类初始化就创建了 还是调用获取实例对象的时候才进行创建
    44.     */
    45.    public static void otherMethod() {
    46.        System.out.println("判断是懒汉式还是饿汉式");
    47.   }
    48.    /**
    49.     * 解决反序列化
    50.     * @return 实例对象
    51.     *
    52.     */
    53.    public Object readResolve() {
    54.        return instance;
    55.   }
    56. }

    这个代码可以这样理解:

    public static Singleton04 getSingleton01() {

         if (instance == null) {

    synchronized (Singleton04.class){

            if (instance == null) {

                      instance = new Singleton04();

              }

    }

    //里面这一块是进行加锁放在出现上面的情况

    //外面的if判断是进行代码的优化

    //我们这个线程锁只是还没创建对象实例的情况出现,因此加上if后

    //我们就保证了后序的调用无需进行加锁操作,加快了效率

         }

    return instance;

    }

    2.5 内部类懒汉式(推荐使用)

    1. package com.sofwin.singletonDemo;
    2. import java.io.Serializable;
    3. /**
    4. * @packageName: com.sofwin.singletonDemo
    5. * @author: wentao
    6. * @date: 2022/11/21 10:00
    7. * @version: 1.0
    8. * @email 1660420659@qq.com
    9. * @description: 单例模式-内部类懒汉式
    10. *
    11. * 要点:
    12. *     内部类不会随着玩不类的加载而初始化
    13. *     它是单独进行初始化和加载的,并且静态成员的赋值,都是在静态代码块中的,是底层保证线程安全的
    14. *     只有调用内部类的类变量的时候,才进行初始化,实现懒汉式模式
    15. */
    16. public class Singleton05 implements Serializable {
    17.    /**
    18.     * 构造器私有化
    19.     */
    20.    private Singleton05() {
    21.        System.out.println("private Singleton01");
    22.   }
    23.    private static class Holder {
    24.        static  Singleton05 instance = new Singleton05();
    25.   }
    26.    public static Singleton05 getSingleton01() {
    27.        return Holder.instance;
    28.   }
    29.    /**
    30.     * 通过调用静态方法,实现类的初始化
    31.     * 判断实例对象是类初始化就创建了 还是调用获取实例对象的时候才进行创建
    32.     */
    33.    public static void otherMethod() {
    34.        System.out.println("判断是懒汉式还是饿汉式");
    35.   }
    36. }

    3、jdk中的体现

    1、饿汉式的单例模式

    System的gc() 垃圾回收机制中的Runtime.getRuntime().gc()

    1. public class Runtime {
    2.   private static Runtime currentRuntime = new Runtime();
    3.   /**
    4.     * Returns the runtime object associated with the current Java application.
    5.     * Most of the methods of class Runtime are instance
    6.     * methods and must be invoked with respect to the current runtime object.
    7.     *
    8.     * @return the Runtime object associated with the current
    9.     *         Java application.
    10.     */
    11.   public static Runtime getRuntime() {
    12.       return currentRuntime;
    13.   }
    14.   /** Don't let anyone else instantiate this class */
    15.   private Runtime() {}

    2、枚举饿汉式

    Comparator的naturalOrder()

    3、懒汉式的DCL(双检锁)

    System中的cons成员变量

    4、内部类懒汉式

    Collections中的emptyListInterator() 

  • 相关阅读:
    sql注入(5), sqlmap工具
    JVM虚拟机:通过日志学习PS+PO垃圾回收器
    C++指针解读(8) -- 指针数组和二重指针
    基于遗传算法的红绿灯间隔时间优化(代码完整,数据齐全)
    如何使用grafana 下JSON API访问展示接口数据
    《uni-app》一个非canvas的飞机对战小游戏实现-子弹模型的实现
    汉诺塔问题(java)
    2023.9.23 关于 HTTP 详解
    服务器简单介绍
    结构光三维重建(一)条纹结构光三维重建
  • 原文地址:https://blog.csdn.net/weixin_52574640/article/details/127972850