• 设计模式——创建型模式(单例模式)


    目录

    1.饿汉式

    1.1 静态变量创建类对象

    1.2 静态代码块中创建该类对象

    2.懒汉式

    2.1线程不安全

    2.2 线程安全

    2.3 双重检查方式

    2.4 静态内部类方式

    测试类

    3. 枚举单例

    3.1 枚举方式

    3.2 原理

    4.破坏单例模式

    4.1序列化反序列化破坏

    4.2反射破坏

    5.解决单例模式破坏

    5.1解决序列化反序列化破坏

    5.2解决反射破坏

    5.3还可以第二次用反射破坏


    1.饿汉式

    1.1 静态变量创建类对象

    1. /**
    2. * 饿汉式:静态变量创建类的对象
    3. */
    4. public class Singleton {
    5. //私有构造方法
    6. private Singleton() {}
    7. //在成员位置创建该类的对象
    8. private static Singleton instance = new Singleton();
    9. //对外提供静态方法获取该对象
    10. public static Singleton getInstance() {
    11. return instance;
    12. }
    13. }
    14. public class Client {
    15. public static void main(String[] args) {
    16. Singleton instance1 = Singleton.getInstance();
    17. Singleton instance2 = Singleton.getInstance();
    18. System.out.println(instance1 == instance2);//true
    19. }
    20. }

    1.2 静态代码块中创建该类对象

    1. /**
    2. * 饿汉式:在静态代码块中创建该类对象
    3. */
    4. public class Singleton {
    5. //私有构造方法
    6. private Singleton() {}
    7. //在成员位置创建该类的对象
    8. private static Singleton instance;
    9. static {
    10. instance = new Singleton();
    11. }
    12. //对外提供静态方法获取该对象
    13. public static Singleton getInstance() {
    14. return instance;
    15. }
    16. }
    17. public class Client {
    18. public static void main(String[] args) {
    19. Singleton instance1 = Singleton.getInstance();
    20. Singleton instance2 = Singleton.getInstance();
    21. System.out.println(instance1 == instance2);//true
    22. }
    23. }

    2.懒汉式

    2.1线程不安全

    1. /**
    2. * 懒汉式:线程不安全
    3. */
    4. public class Singleton {
    5. //私有构造方法
    6. private Singleton() {
    7. System.out.println(Thread.currentThread().getName() + "OK");
    8. }
    9. //在成员位置创建该类的对象
    10. private static Singleton instance;
    11. //对外提供静态方法获取该对象
    12. public static Singleton getInstance() {
    13. if (instance == null) {
    14. instance = new Singleton();
    15. }
    16. return instance;
    17. }
    18. }

    2.2 线程安全

    1. /**
    2. * 懒汉式:线程安全!!!
    3. */
    4. public class Singleton {
    5. //私有构造方法
    6. private Singleton() {
    7. System.out.println(Thread.currentThread().getName()+"OK");
    8. }
    9. //在成员位置创建该类的对象
    10. private static Singleton instance;
    11. //对外提供静态方法获取该对象
    12. public static synchronized Singleton getInstance() {
    13. if(instance == null) {
    14. instance = new Singleton();
    15. }
    16. return instance;
    17. }
    18. }

    2.3 双重检查方式

    1. /**
    2. * 双重检查方式
    3. * 对于getInstance()方法来说,绝大部分的操作都是读操作,读操作是线程安全的,
    4. * 所以我们没必让每个线程必须持有锁才能调用该方法,我们需要调整加锁的时机。
    5. *
    6. * 在多线程的情况下,可能会出现空指针问题,出现问题的原因是JVM在实例化对象的时候会进行优化和指令重排序操作。
    7. * 需要使用volatile关键字,volatile关键字可以保证可见性和有序性。
    8. */
    9. public class Singleton {
    10. //私有构造方法
    11. private Singleton() {
    12. System.out.println(Thread.currentThread().getName()+"OK");
    13. }
    14. private static volatile Singleton instance;//volatile关键字可以保证可见性和有序性。
    15. //对外提供静态方法获取该对象
    16. public static Singleton getInstance() {
    17. //第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实例
    18. if(instance == null) {
    19. synchronized (Singleton.class) {
    20. //抢到锁之后再次判断是否为null
    21. if(instance == null) {
    22. instance = new Singleton();
    23. /**
    24. * 1.分配内存空间
    25. * 2.执行构造方法,初始化对象
    26. * 3.把这个对象指向这个空间
    27. *
    28. * A线程:由于指令重排,执行顺序为132
    29. * B线程:发现空间已经占用了,直接返回,但此时LazyMan还没有完成构造,出现空指针
    30. */
    31. }
    32. }
    33. }
    34. return instance;
    35. }
    36. }

    2.4 静态内部类方式

    1. /**
    2. * 静态内部类方式
    3. *
    4. * ​第一次加载Singleton类时不会去初始化INSTANCE,
    5. * 只有第一次调用getInstance,虚拟机加载SingletonHolder并初始化INSTANCE,
    6. * 这样不仅能确保线程安全,也能保证Singleton类的唯一性。
    7. *
    8. * 静态内部类单例模式是一种优秀的单例模式,是开源项目中比较常用的一种单例模式。
    9. * 在没有加任何锁的情况下,保证了多线程下的安全,并且没有任何性能影响和空间的浪费。
    10. */
    11. public class Singleton {
    12. //私有构造方法
    13. private Singleton() {
    14. System.out.println(Thread.currentThread().getName() + "OK");
    15. }
    16. //静态内部类
    17. private static class SingletonHolder {
    18. private static final Singleton INSTANCE = new Singleton();
    19. }
    20. //对外提供静态方法获取该对象
    21. public static Singleton getInstance() {
    22. return SingletonHolder.INSTANCE;
    23. }
    24. }

    测试类

    1. public class Client {
    2. public static void main(String[] args) {
    3. for (int i = 0; i < 10; i++) {
    4. new Thread(()->{
    5. Singleton.getInstance();
    6. }).start();
    7. }
    8. }
    9. }

    3. 枚举单例

    3.1 枚举方式

    1. /**
    2. * 枚举方式
    3. */
    4. public enum Singleton {
    5. INSTANCE;
    6. public static Singleton getInstance() {
    7. return INSTANCE;
    8. }
    9. }
    10. public class Client {
    11. public static void main(String[] args) {
    12. Singleton instance1 = Singleton.INSTANCE;
    13. Singleton instance2 = Singleton.INSTANCE;
    14. System.out.println(instance1==instance2);//true
    15. }
    16. }

    3.2 原理

    1. import java.lang.reflect.Constructor;
    2. import java.lang.reflect.InvocationTargetException;
    3. /**
    4. * 尝试反射破坏
    5. */
    6. public class Test {
    7. public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    8. Constructor declaredConstructor = Singleton.class.getDeclaredConstructor(null);
    9. declaredConstructor.setAccessible(true);
    10. Singleton instance = declaredConstructor.newInstance();
    11. System.out.println(instance);//NoSuchMethodException,没有空参构造方法
    12. }
    13. }

    1. import java.lang.reflect.Constructor;
    2. import java.lang.reflect.InvocationTargetException;
    3. /**
    4. * 尝试反射破坏
    5. */
    6. public class Test2 {
    7. public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    8. Singleton instance1 = Singleton.getInstance();
    9. Constructor declaredConstructor = Singleton.class.getDeclaredConstructor(String.class, int.class);
    10. declaredConstructor.setAccessible(true);
    11. Singleton instance2 = declaredConstructor.newInstance();
    12. System.out.println(instance1);
    13. System.out.println(instance2);//IllegalArgumentException: Cannot reflectively create enum objects
    14. }
    15. }

    4.破坏单例模式

    4.1序列化反序列化破坏

    1. import java.io.Serializable;
    2. /**
    3. * 静态内部类方式
    4. */
    5. public class Singleton implements Serializable{
    6. //私有构造方法
    7. private Singleton() {
    8. }
    9. //静态内部类
    10. private static class SingletonHolder {
    11. private static final Singleton INSTANCE = new Singleton();
    12. }
    13. //对外提供静态方法获取该对象
    14. public static Singleton getInstance() {
    15. return SingletonHolder.INSTANCE;
    16. }
    17. }
    1. import java.io.FileInputStream;
    2. import java.io.FileOutputStream;
    3. import java.io.ObjectInputStream;
    4. import java.io.ObjectOutputStream;
    5. /**
    6. * 序列化反序列化破坏单例模式
    7. */
    8. public class Test {
    9. public static void main(String[] args) throws Exception {
    10. //往文件中写对象
    11. // writeObject2File();
    12. //从文件中读取对象
    13. Singleton s1 = readObjectFromFile();
    14. Singleton s2 = readObjectFromFile();
    15. //判断两个反序列化后的对象是否是同一个对象
    16. System.out.println(s1 == s2);//false
    17. }
    18. /**
    19. * 从文件中读数据
    20. * @return
    21. * @throws Exception
    22. */
    23. private static Singleton readObjectFromFile() throws Exception {
    24. //1.创建对象输入流对象
    25. ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
    26. //2.第一个读取Singleton对象
    27. Singleton instance = (Singleton) ois.readObject();
    28. //3.释放资源
    29. ois.close();
    30. //4.返回结果
    31. return instance;
    32. }
    33. /**
    34. * 向文件中写数据
    35. * @throws Exception
    36. */
    37. public static void writeObject2File() throws Exception {
    38. //1.获取Singleton类的对象
    39. Singleton instance = Singleton.getInstance();
    40. //2.创建对象输出流
    41. ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
    42. //3.将instance对象写出到文件中
    43. oos.writeObject(instance);
    44. //4.释放资源
    45. oos.close();
    46. }
    47. }

    4.2反射破坏

    1. import java.io.Serializable;
    2. /**
    3. * 静态内部类方式
    4. */
    5. public class Singleton implements Serializable{
    6. //私有构造方法
    7. private Singleton() {
    8. }
    9. //静态内部类
    10. private static class SingletonHolder {
    11. private static final Singleton INSTANCE = new Singleton();
    12. }
    13. //对外提供静态方法获取该对象
    14. public static Singleton getInstance() {
    15. return SingletonHolder.INSTANCE;
    16. }
    17. }
    1. import java.lang.reflect.Constructor;
    2. /**
    3. * 反射破坏单例模式
    4. */
    5. public class Test {
    6. public static void main(String[] args) throws Exception {
    7. //获取Singleton类的字节码对象
    8. Class c = Singleton.class;
    9. //获取Singleton类的私有无参构造方法对象
    10. Constructor constructor = c.getDeclaredConstructor();
    11. //取消访问检查
    12. constructor.setAccessible(true);
    13. //创建Singleton类的对象s1
    14. Singleton s1 = (Singleton) constructor.newInstance();
    15. //创建Singleton类的对象s2
    16. Singleton s2 = (Singleton) constructor.newInstance();
    17. //判断通过反射创建的两个Singleton对象是否是同一个对象
    18. System.out.println(s1 == s2);//false
    19. }
    20. }

    5.解决单例模式破坏

    5.1解决序列化反序列化破坏

    1. import java.io.Serializable;
    2. /**
    3. * 静态内部类方式:解决序列化反序列化破解单例模式
    4. */
    5. public class Singleton implements Serializable{
    6. //私有构造方法
    7. private Singleton() {
    8. }
    9. //静态内部类
    10. private static class SingletonHolder {
    11. private static final Singleton INSTANCE = new Singleton();
    12. }
    13. //对外提供静态方法获取该对象
    14. public static Singleton getInstance() {
    15. return SingletonHolder.INSTANCE;
    16. }
    17. /**
    18. * 下面是为了解决序列化反序列化破解单例模式
    19. * 当进行反序列化时,会自动调用该方法,将该方法的返回值直接返回
    20. */
    21. private Object readResolve() {
    22. return SingletonHolder.INSTANCE;
    23. }
    24. }

    5.2解决反射破坏

    1. /**
    2. * 静态内部类方式:反射方式破解单例的解决方法
    3. */
    4. public class Singleton {
    5. private static boolean flag = false;
    6. //私有构造方法
    7. private Singleton() {
    8. if (flag) {//如果是true,不是第一次创建
    9. throw new RuntimeException("不能创建多个对象");
    10. }
    11. flag = true;
    12. }
    13. private static volatile Singleton instance;
    14. //对外提供静态方法获取该对象
    15. public static Singleton getInstance() {
    16. if (instance != null) {
    17. return instance;
    18. }
    19. synchronized (Singleton.class) {
    20. if (instance != null) {
    21. return instance;
    22. }
    23. instance = new Singleton();
    24. return instance;
    25. }
    26. }
    27. }

    5.3还可以第二次用反射破坏

    1. import java.lang.reflect.Constructor;
    2. import java.lang.reflect.Field;
    3. /**
    4. * 第二次用反射破坏单例模式
    5. */
    6. public class Test {
    7. public static void main(String[] args) throws Exception {
    8. //获取Singleton类的字节码对象
    9. Class c = Singleton.class;
    10. //获取Singleton类的私有无参构造方法对象
    11. Constructor constructor = c.getDeclaredConstructor();
    12. //取消访问检查
    13. constructor.setAccessible(true);
    14. //创建Singleton类的对象s1
    15. Singleton s1 = (Singleton) constructor.newInstance();
    16. //破坏单例
    17. Field flag = c.getDeclaredField("flag");
    18. flag.setAccessible(true);
    19. flag.set(s1, false);
    20. //创建Singleton类的对象s2
    21. Singleton s2 = (Singleton) constructor.newInstance();
    22. //判断通过反射创建的两个Singleton对象是否是同一个对象
    23. System.out.println(s1 == s2);//false
    24. }
    25. }

  • 相关阅读:
    学习笔记7--卫星定位技术(下)
    基于单片机的学生视力保护仪
    k8s部署针对外部服务器的prometheus服务
    js数据结构算法[栈操作]
    享元模式模式简介
    supervisor—进程管理神器
    Dubbo面试系列问题总结
    神经网络结构设计,神经网络架构设计
    【JVM】内存快照分析工具Jprofiler
    如何进行MDM的产品测试
  • 原文地址:https://blog.csdn.net/m0_63544124/article/details/125991056