单例模式

单例模式

什么是单例模式?

  • 单例模式是一种创建型设计模式, 让你能够保证一个类只有一个实例, 并提供一个访问该实例的全局节点。

实现方式

  1. 在类中添加一个私有静态成员变量用于保存单例实例。
  2. 声明一个公有静态构建方法用于获取单例实例。
  3. 在静态方法中实现"延迟初始化"。 该方法会在首次被调用时创建一个新对象, 并将其存储在静态成员变量中。 此后该方法每次被调用时都返回该实例。
  4. 将类的构造函数设为私有。 类的静态方法仍能调用构造函数, 但是其他对象不能调用。
  5. 检查客户端代码, 将对单例的构造函数的调用替换为对其静态构建方法的调用。

应用场景

  • 如果程序中的某个类对于所有客户端只有一个可用的实例, 可以使用单例模式。
  • 如果你需要更加严格地控制全局变量, 可以使用单例模式。
  • java.lang.Runtime应用了单例设计模式。

单例模式优缺点

优点 缺点
可以保证一个类只有一个实例。 违反了单一职责原则。
获得一个指向该实例的全局访问节点。 单例模式可能掩盖不良设计, 比如程序各组件之间相互了解过多等。
仅在首次请求单例对象时对其进行初始化。 该模式在多线程环境下需要进行特殊处理, 避免多个线程多次创建单例对象。
单例的客户端代码单元测试可能会比较困难, 因为许多测试框架以基于继承的方式创建模拟对象。 由于单例类的构造函数是私有的, 而且绝大部分语言无法重写静态方法, 所以你需要想出仔细考虑模拟单例的方法。 要么干脆不编写测试代码, 或者不使用单例模式。

代码演示

饿汉式(普通)

  • package com.lyc.study.designMode.singleton;
    
    /**
     * 饿汉式: 单例类的实例是在类加载时创建的,会造成资源浪费;线程安全的
     *
     * @author 李云晨
     * @date 2022/07/24 14:48
     **/
    public class Singleton {
    
        private static final Singleton instance = new Singleton();
    
        private Singleton () {}
    
        public static Singleton getInstance () {
            return instance;
        }
    }
    
    class Client {
    
        public static void main(String[] args) throws InterruptedException {
    
            new Thread(() -> {
                System.out.println(Singleton.getInstance());
            }).start();
            
            new Thread(() -> {
                System.out.println(Singleton.getInstance());
            }).start();
        }
    }
    

饿汉式(静态代码块)

package com.lyc.study.designMode.singleton;

/**
 * 饿汉式: 单例类的实例是在类加载时创建的,会造成资源浪费;线程安全的
 *
 * @author 李云晨
 * @date 2022/07/24 14:48
 **/
public class Singleton {

    private static Singleton instance;

    static {
        // 可以定义异常抛出
        instance = new Singleton();
    }

    private Singleton () {}

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

class Client {

    public static void main(String[] args) throws InterruptedException {

        new Thread(() -> {
            System.out.println(Singleton.getInstance());
        }).start();

        new Thread(() -> {
            System.out.println(Singleton.getInstance());
        }).start();
    }
}

懒汉式(单线程-Y,多线程-N)

  • package com.lyc.study.designMode.singleton;
    
    import java.util.Objects;
    
    /**
     * @author 李云晨
     * @date 2022/07/24 14:48
     **/
    public class Singleton {
    
        private static Singleton instance;
    
        public String value;
    
        private Singleton (String value) {
            // 以此来模拟慢加载
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.value = value;
        }
    
        public static Singleton getInstance (String value) {
            if (Objects.isNull(instance)) {
                instance = new Singleton(value);
            }
            return instance;
        }
    }
    
    class Client {
    
        public static void main(String[] args) {
            /**
             * 若两个值均为实例A,则是单例模式
             * 若两个值不等,则创建了两个不同实例
             */
            new Thread(() -> {
                Singleton a = Singleton.getInstance("实例A");
                System.out.println(a.value);
            }, "线程A").start();
    
            new Thread(() -> {
                Singleton b = Singleton.getInstance("实例B");
                System.out.println(b.value);
            }, "线程B").start();
        }
    }
    

懒汉式(多线程-Y)

package com.lyc.study.designMode.singleton;

import java.util.Objects;

/**
 * @author 李云晨
 * @date 2022/07/24 14:48
 **/
public class Singleton {
    /**
     * 字段必须声明为volatile,这样双重检查锁才能工作
     */
    private volatile static Singleton instance;

    public String value;

    private Singleton (String value) {
        // 以此来模拟慢加载
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.value = value;
    }

    public static Singleton getInstance (String value) {
        /**
         * 双重检查锁定 (DCL)
         */
        if (Objects.isNull(instance)) {
            synchronized (Singleton.class) {
                if (Objects.isNull(instance)) {
                    instance = new Singleton(value);
                }
            }
        }
        return instance;
    }
}

class Client {

    public static void main(String[] args) {
        /**
         * 若两个值均为实例A,则是单例模式
         * 若两个值不等,则创建了两个不同实例
         */
        new Thread(() -> {
            Singleton a = Singleton.getInstance("实例A");
            System.out.println(a.value);
        }, "线程A").start();

        new Thread(() -> {
            Singleton b = Singleton.getInstance("实例B");
            System.out.println(b.value);
        }, "线程B").start();
    }
}

私有静态内部类

package com.lyc.study.designMode.singleton;

/**
 * InnerSingleton类不会加载到内存中,只有当有人调用getInstance方法时,才会加载此类并创建单例类实例。
 *
 * @author 李云晨
 * @date 2022/07/24 14:48
 **/
public class Singleton {

    private Singleton () {}

    private static class InnerSingleton {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance () {
        return InnerSingleton.INSTANCE;
    }
}

class Client {

    public static void main(String[] args) throws InterruptedException {

        new Thread(() -> {
            System.out.println(Singleton.getInstance());
        }).start();

        new Thread(() -> {
            System.out.println(Singleton.getInstance());
        }).start();
    }
}

反射破坏单例

  • package com.lyc.study.designMode.singleton;
    
    import java.lang.reflect.Constructor;
    
    /**
     * 反射破坏单例
     *
     * @author 李云晨
     * @date 2022/07/24 14:48
     **/
    public class Singleton {
    
        private Singleton() {
        }
    
        private static class InnerSingleton {
            private static final Singleton INSTANCE = new Singleton();
        }
    
        public static Singleton getInstance() {
            return InnerSingleton.INSTANCE;
        }
    }
    
    class Client {
    
        public static void main(String[] args) {
    
            Singleton a = Singleton.getInstance();
            Singleton b = null;
            Constructor<?>[] constructors = Singleton.class.getDeclaredConstructors();
            for (Constructor<?> constructor : constructors) {
                try {
                    constructor.setAccessible(true);
                    b = (Singleton)constructor.newInstance();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                break;
            }
            // false
            System.out.println(a == b);
        }
    }
    

枚举

  • package com.lyc.study.designMode.singleton;
    
    /**
     * 枚举实例单例模式
     *
     * @author 李云晨
     * @date 2022/07/24 14:48
     **/
    public enum Singleton {
    
        INSTANCE;
    
    }
    
    class Client {
        public static void main(String[] args) {
            Singleton a = Singleton.INSTANCE;
            Singleton b = Singleton.INSTANCE;
            System.out.println(a == b);
        }
    }
    

序列化破坏单例

  • package com.lyc.study.designMode.singleton;
    
    import java.io.*;
    
    /**
     * 反序列化破坏单例
     *
     * @author 李云晨
     * @date 2022/07/24 14:48
     **/
    public class Singleton implements Serializable {
    
        private static final long serialVersionUID = 763534173627372604L;
    
        private Singleton () {}
    
        private static class InnerSingleton {
            private static final Singleton INSTANCE = new Singleton();
        }
    
        public static Singleton getInstance () {
            return InnerSingleton.INSTANCE;
        }
      
    }
    
    class Client {
    
        public static void main(String[] args) {
    
            Singleton a = Singleton.getInstance();
            Singleton b = null;
            try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("实例A"))) {
                out.writeObject(a);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("实例A"))) {
                b = (Singleton) in.readObject();;
            } catch (Exception e) {
                e.printStackTrace();
            }
    		// false
            System.out.println(a == b);
        }
    }
    

序列化破坏单例(解决方案)

  • package com.lyc.study.designMode.singleton;
    
    import java.io.*;
    
    /**
     * 单例类增加readResolve方法
     *
     * @author 李云晨
     * @date 2022/07/24 14:48
     **/
    public class Singleton implements Serializable {
    
        private static final long serialVersionUID = 763534173627372604L;
    
        private Singleton () {}
    
        private static class InnerSingleton {
            private static final Singleton INSTANCE = new Singleton();
        }
    
        public static Singleton getInstance () {
            return InnerSingleton.INSTANCE;
        }
    
        /**
         * 当我们通过反序列化readObject()方法获取对象时会去寻找readResolve()方法。
         * 如果该方法不存在则直接返回新对象,如果该方法存在则按该方法的内容返回对象。
         * 以确保如果我们之前实例化了单例对象,就返回该对象。
         * 如果我们之前没有实例化单例对象,则会返回null。
         */
        protected Object readResolve() {
            return getInstance();
        }
    }
    
    class Client {
    
        public static void main(String[] args) {
    
            Singleton a = Singleton.getInstance();
            Singleton b = null;
            try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("实例A"))) {
                out.writeObject(a);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("实例A"))) {
                b = (Singleton) in.readObject();;
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            System.out.println(a == b);
        }
    }
    
posted @ 2022-07-24 21:27  加油,每天一点小进步!  阅读(37)  评论(0)    收藏  举报