03-JAVA设计模式-单例模式详解

这篇具有很好参考价值的文章主要介绍了03-JAVA设计模式-单例模式详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

单例模式

单例模式(Singleton Pattern)是设计模式中的一种,它确保一个类仅有一个实例,并提供一个全局访问点来访问该实例。这种设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
单例模式的应用场景十分广泛,主要涉及需要频繁使用某个对象而又不想重复创建的情况:

常见应用场景:

  • Windows的任务管理器和回收站:这两个都是只能打开一个实例的应用程序,符合单例模式的特点。
  • 网站的计数器:由于需要同步访问,使用单例模式可以确保计数的准确性。
  • 应用程序的日志应用:一般系统共用一个日志,因此采用单例模式可以方便地对日志进行管理和操作。
  • 数据库连接池:数据库连接是一种昂贵的资源,频繁地创建和关闭数据库连接会带来巨大的性能损耗。采用单例模式,可以维护一个数据库连接的实例,从而避免这种损耗。
  • 多线程的线程池:线程池需要方便地对池中的线程进行控制,使用单例模式可以确保线程池的唯一性,从而方便管理。

单例模式的优点

  1. 节省系统资源:由于单例模式限制了一个类只能有一个实例,这避免了因重复创建对象而浪费的内存和其他系统资源。特别是在处理大型对象或需要消耗大量资源的对象时,单例模式可以显著减少资源消耗。
  2. 提高系统性能:由于无需反复创建对象,单例模式减少了在对象创建和销毁过程中的系统开销。在频繁创建和销毁对象会导致性能问题的场景下,单例模式能有效提升性能。
  3. 简化管理:单例模式提供了一个全局访问点,使得开发者可以方便地访问和操作该类的唯一实例。这简化了对对象的管理和维护,降低了代码的复杂性。
  4. 保证数据一致性:在某些情况下,如配置信息、线程池、数据库连接等,需要确保在整个系统中只有一个实例存在,以保证数据的一致性。单例模式可以确保这些资源在全局范围内只有一个访问点,从而避免了数据不一致的问题。
  5. 易于扩展:虽然单例模式限制了类的实例数量,但它并不限制类的功能扩展。开发者可以在保持单例特性的同时,为类添加新的方法和属性,以满足不断变化的需求。

常见五种单例模式实现方式

1. 饿汉式

实现方式SingletonDemo.java

package demo1;

/**
 *  单例模式:饿汉式
 *  1 提供一个私有静态的SingletonDemo类型属性
 *  2 构造器私有化
 *  3 提供一个public静态的初始化方法getInstance()方法
 *
 * @author Anna.
 * @date 2024/4/5 20:34
 */
public class SingletonDemo {

    // 静态的SingletonDemo类型属性,初始化时,立即加载这个对象
    // 由于JVM类加载规则,加载class时进行初始化,保证了线程安全,但是没有延时加载的优势
    // 因此可能会造成内存浪费,因为无论是否使用到该实例,它都会在类加载时就被创建
    private static SingletonDemo instance = new SingletonDemo();

    // 构造器私有化
    private SingletonDemo() {
    }

    // 提供一个public的初始化方法 getInstance()方法
    public static SingletonDemo getInstance(){
        return instance;
    }
}

单线程测试:

package demo1;

public class SingleThreadedTestClient {
    public static void main(String[] args) {
        // 单线程模式
        SingletonDemo instance1 = SingletonDemo.getInstance();
        SingletonDemo instance2 = SingletonDemo.getInstance();
        System.out.printf("单线程模式下,判断获取的两个单例模式对象,判断是否是同一个对象:%s%n", instance1.hashCode() == instance2.hashCode());
    }
}

执行结果:

03-JAVA设计模式-单例模式详解,技术栈,# Java设计模式,java,设计模式,单例模式

多线程测试:

package demo1;

public class MultithreadingTestClient {
    public static void main(String[] args) {
        // 多线程模式
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                SingletonDemo instance3 = SingletonDemo.getInstance();
                SingletonDemo instance4 = SingletonDemo.getInstance();

                if (instance3.hashCode() != instance4.hashCode()) {
                    System.out.printf("多线程模式下,判断获取的两个单例模式对象,判断是否是同一个对象-线程名称%s:%s%n",
                            Thread.currentThread().getName(), instance3.hashCode() == instance4.hashCode());
                }

            }, "线程" + i).start();
        }
    }
}

执行结果:

03-JAVA设计模式-单例模式详解,技术栈,# Java设计模式,java,设计模式,单例模式

结论:

优点:没有线程安全问题,实现简单。

缺点:可能会造成内存浪费,因为无论是否使用到该实例,它都会在类加载时就被创建。

2. 懒汉式:

实现方式SingletonDemo.java

package demo2;

/**
 * 单例模式:懒汉式
 * 1 提供一个私有的静态SingletonDemo属性
 * 2 构造函数私有化
 * 3 提供一个public静态的初始化方法getInstance()方法
 *
 * @author Anna.
 * @date 2024/4/5 20:59
 */
public class SingletonDemo {

    // 1 提供一个私有的静态SingletonDemo属性
    // 这里在加载时,不进行初始化,调用getInstance()方法时进行初始化。以达到延时加载的目的
    private static SingletonDemo instance;

    // 2 构造函数私有化
    private SingletonDemo(){}

    // 3 提供一个public静态的初始化方法getInstance()方法
    public static SingletonDemo getInstance(){
        // 判断属性是否为空
        if(instance == null){
            // 延时加载,初始化对象
            instance = new SingletonDemo();
        }
        // 返回对象
        return instance;
    }
}

单线程测试:

package demo2;

public class SingleThreadedTestClient {
    public static void main(String[] args) throws InterruptedException {
        // 单线程模式
        System.out.println("========== 单线程测试-懒汉式单例模式是否线程安全 ==========");
        SingletonDemo instance1 = SingletonDemo.getInstance();
        SingletonDemo instance2 = SingletonDemo.getInstance();
        System.out.printf("单线程模式下,判断获取的两个单例模式对象,判断是否是同一个对象:%s%n", instance1.hashCode() == instance2.hashCode());
    }
}

执行结果:

03-JAVA设计模式-单例模式详解,技术栈,# Java设计模式,java,设计模式,单例模式

多线程测试:

package demo2;

public class MultithreadingTestClient {
    public static void main(String[] args) {
        // 多线程模式
        System.out.println("========== 多线程测试-懒汉式单例模式是否线程安全 ==========");
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                SingletonDemo instance3 = SingletonDemo.getInstance();
                SingletonDemo instance4 = SingletonDemo.getInstance();

                if (instance3.hashCode() != instance4.hashCode()) {
                    System.out.printf("多线程模式下,判断获取的两个单例模式对象,判断是否是同一个对象-%s:%s%n",
                            Thread.currentThread().getName(), instance3.hashCode() == instance4.hashCode());
                }

            }, "线程" + i).start();
        }
    }
}

执行结果:多线程模式下,多执行几次我们会发现,有时懒汉式单例模式,拿到的并不是同一个对象

03-JAVA设计模式-单例模式详解,技术栈,# Java设计模式,java,设计模式,单例模式

优点:延时加载,节省内存。

缺点:在多线程环境下存在线程安全问题,需要通过加锁来保证实例的唯一性,这可能会影响并发性能。

// 加锁
public static synchronized SingletonDemo getInstance(){
    // 判断属性是否为空
    if(instance == null){
        // 延时加载,初始化对象
        instance = new SingletonDemo();
    }
    // 返回对象
    return instance;
}

3. 双重检锁方式:

实现方式SingletonDemo.java

package demo3;

/**
 * 单例模式:双重检锁方式
 * 1 提供一个私有的静态SingletonDemo属性
 * 2 构造函数私有化
 * 3 提供一个public静态的初始化方法getInstance()方法
 *
 * @author Anna.
 * @date 2024/4/5 20:59
 */
public class SingletonDemo {

    // 1 提供一个私有的静态SingletonDemo属性
    // 这里在加载时,不进行初始化,调用getInstance()方法时进行初始化。以达到延时加载的目的
    // 使用volatile修饰instance,保证多线程环境下instance的可见性
    private volatile static SingletonDemo instance;

    // 2 构造函数私有化
    private SingletonDemo() {
    }

    // 3 提供一个public静态的初始化方法getInstance()方法
    public static SingletonDemo getInstance() {
        // 判断属性是否为空
        if (instance == null) {
            // 使用 synchronized 代码块 进行加锁
            synchronized (SingletonDemo.class) {
                if (instance == null) {
                    // 延时加载,初始化对象
                    instance = new SingletonDemo();
                }
            }

        }
        // 返回对象
        return instance;
    }
}

单线程测试:

package demo3;

public class SingleThreadedTestClient {
    public static void main(String[] args) throws InterruptedException {
        // 单线程模式·
        System.out.println("========== 单线程测试-双重检锁方式单例模式是否线程安全 ==========");
        SingletonDemo instance1 = SingletonDemo.getInstance();
        SingletonDemo instance2 = SingletonDemo.getInstance();
        System.out.printf("单线程模式下,判断获取的两个单例模式对象,判断是否是同一个对象:%s%n", instance1.hashCode() == instance2.hashCode());
    }
}

执行结果:

03-JAVA设计模式-单例模式详解,技术栈,# Java设计模式,java,设计模式,单例模式

多线程测试:

package demo3;

public class MultithreadingTestClient {
    public static void main(String[] args) {
        // 多线程模式
        System.out.println("========== 多线程测试-双重检锁方式单例模式是否线程安全 ==========");
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                SingletonDemo instance3 = SingletonDemo.getInstance();
                SingletonDemo instance4 = SingletonDemo.getInstance();

                if (instance3.hashCode() != instance4.hashCode()) {
                    System.out.printf("多线程模式下,判断获取的两个单例模式对象,判断是否是同一个对象-%s:%s%n",
                            Thread.currentThread().getName(), instance3.hashCode() == instance4.hashCode());
                }

            }, "线程" + i).start();
        }
    }
}

执行结果:

03-JAVA设计模式-单例模式详解,技术栈,# Java设计模式,java,设计模式,单例模式

优点:结合了饿汉式和懒汉式的优点,既实现了懒加载,又解决了线程安全问题。

缺点:双重检锁的实现虽然巧妙,但也比较复杂,容易出现错误。同时,从Java 5开始,JVM内部对synchronized的实现做了优化,使得这种方式的性能优势不再明显。因此不建议使用

4. 静态内部类式:

实现方式SingletonDemo.java

package demo4;

/**
 * 单例模式:静态内部类方式
 * 
 * 1 定义一个静态内部类,内部类中提供一个静态final的单例对象属性
 * 2 构造函数私有化
 * 3 提供一个public静态的初始化方法getInstance()方法
 *
 * @author Anna.
 * @date 2024/4/5 21:31
 */
public class SingletonDemo {

    // 1 定义一个静态内部类,内部类中提供一个静态final的单例对象属性
    static class SingletonDemoInstance{
        // 静态内部类 利用 JVM类加载器 加载类时 首先初始化 类中静态常量到常量池中且一个class只会被初始化一次这一特性保证线程安全
        private static final SingletonDemo instance = new SingletonDemo();
    }

    // 2 构造函数私有化
    private SingletonDemo() {
    }

    // 3 提供一个public静态的初始化方法getInstance()方法
    public static SingletonDemo getInstance() {
        return SingletonDemoInstance.instance;
    }
}

单线程测试:

package demo4;

public class SingleThreadedTestClient {
    public static void main(String[] args) throws InterruptedException {
        // 单线程模式·
        System.out.println("========== 单线程测试-静态内部类方式单例模式是否线程安全 ==========");
        SingletonDemo instance1 = SingletonDemo.getInstance();
        SingletonDemo instance2 = SingletonDemo.getInstance();
        System.out.printf("单线程模式下,判断获取的两个单例模式对象,判断是否是同一个对象:%s%n", instance1.hashCode() == instance2.hashCode());
    }
}

执行结果:

03-JAVA设计模式-单例模式详解,技术栈,# Java设计模式,java,设计模式,单例模式

多线程测试:

package demo4;

public class MultithreadingTestClient {
    public static void main(String[] args) {
        // 多线程模式
        System.out.println("========== 多线程测试-静态内部类方式单例模式是否线程安全 ==========");
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                SingletonDemo instance3 = SingletonDemo.getInstance();
                SingletonDemo instance4 = SingletonDemo.getInstance();

                if (instance3.hashCode() != instance4.hashCode()) {
                    System.out.printf("多线程模式下,判断获取的两个单例模式对象,判断是否是同一个对象-%s:%s%n",
                            Thread.currentThread().getName(), instance3.hashCode() == instance4.hashCode());
                }

            }, "线程" + i).start();
        }
    }
}

执行结果:

03-JAVA设计模式-单例模式详解,技术栈,# Java设计模式,java,设计模式,单例模式

优点:线程安全,实现简单,且实现了懒加载,延时加载。

缺点:相较于双重检查锁定式,其实现方式稍微复杂一些。

5. 枚举式:

实现方式SingletonDemo.java

package demo5;

/**
 * 单例模式:枚举
 *
 * 1 定义一个枚举的元素
 * 2 枚举本省就是单例模式。有JVM从根本上提供保证。避免通过反射和反序列化的漏洞
 * 3 如需使用提供相应public 方法即可
 *
 * @author Anna.
 * @date 2024/4/5 21:31
 */
public enum SingletonDemo {
    /** 定义一个枚举元素,它就代表了单例的一个实例*/
    INSTANCD;

    public void test(){System.out.println("枚举单例内部方法调用了");}
}

单线程测试:

package demo5;

public class SingleThreadedTestClient {
    public static void main(String[] args) throws InterruptedException {
        // 单线程模式·
        System.out.println("========== 单线程测试枚举单例模式是否线程安全 ==========");
        SingletonDemo instance1 = SingletonDemo.INSTANCD;
        SingletonDemo instance2 = SingletonDemo.INSTANCD;
        System.out.printf("单线程模式下,判断获取的两个单例模式对象,判断是否是同一个对象:%s%n", instance1.hashCode() == instance2.hashCode());
        
        // 调用方式
        SingletonDemo.INSTANCD.test();
    }
}

执行结果:

03-JAVA设计模式-单例模式详解,技术栈,# Java设计模式,java,设计模式,单例模式

多线程测试:

package demo5;

public class MultithreadingTestClient {
    public static void main(String[] args) {
        // 多线程模式
        System.out.println("========== 多线程测试枚举单例模式是否线程安全 ==========");
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                SingletonDemo instance3 = SingletonDemo.INSTANCD;
                SingletonDemo instance4 = SingletonDemo.INSTANCD;

                if (instance3.hashCode() != instance4.hashCode()) {
                    System.out.printf("多线程模式下,判断获取的两个单例模式对象,判断是否是同一个对象-%s:%s%n",
                            Thread.currentThread().getName(), instance3.hashCode() == instance4.hashCode());
                }

            }, "线程" + i).start();
        }
    }
}

执行结果:

03-JAVA设计模式-单例模式详解,技术栈,# Java设计模式,java,设计模式,单例模式

优点:线程安全,实现简单,防止反射和反序列化漏洞。

缺点:没有实现懒加载,类加载时就创建了对象实例。

当然通过Spring,也可以创建单例模式(后面介绍)

多线程模式下效率测试

package demo6;

import demo1.SingletonDemo;

import java.util.concurrent.CountDownLatch;

/**
 * 测试5中单例模式 多线程下的执行效率
 *
 * @author Anna.
 * @date 2024/4/5 21:53
 */
public class TestClient {
    public static void main(String[] args) throws InterruptedException {
        long start = System.currentTimeMillis();
        int threadNum = 10;
        CountDownLatch countDownLatch1 = new CountDownLatch(threadNum);
        for (int i = 0; i < threadNum; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000000000; j++) {
                    SingletonDemo instance = SingletonDemo.getInstance();
                }
                countDownLatch1.countDown();
            }).start();
        }
        countDownLatch1.await();
        long end = System.currentTimeMillis();
        System.out.printf("饿汉式-%s个多线程各自创建1000000000次-耗时:%s%n", threadNum, end - start);

        CountDownLatch countDownLatch2 = new CountDownLatch(threadNum);
        for (int i = 0; i < threadNum; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000000000; j++) {
                    demo2.SingletonDemo instance = demo2.SingletonDemo.getInstance();
                }
                countDownLatch2.countDown();
            }).start();
        }
        countDownLatch2.await();
        end = System.currentTimeMillis();
        System.out.printf("懒汉式-%s个多线程各自创建1000000000次-耗时:%s%n", threadNum, end - start);

        CountDownLatch countDownLatch3 = new CountDownLatch(threadNum);
        for (int i = 0; i < threadNum; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000000000; j++) {
                    demo2.SingletonDemo instance = demo2.SingletonDemo.getInstance();
                }
                countDownLatch3.countDown();
            }).start();
        }
        countDownLatch3.await();
        end = System.currentTimeMillis();
        System.out.printf("双重检锁方式-%s个多线程各自创建1000000000次-耗时:%s%n", threadNum, end - start);

        CountDownLatch countDownLatch4 = new CountDownLatch(threadNum);
        for (int i = 0; i < threadNum; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000000000; j++) {
                    demo2.SingletonDemo instance = demo2.SingletonDemo.getInstance();
                }
                countDownLatch4.countDown();
            }).start();
        }
        countDownLatch4.await();
        end = System.currentTimeMillis();
        System.out.printf("静态内部类方式-%s个多线程各自创建1000000000次-耗时:%s%n", threadNum, end - start);

        CountDownLatch countDownLatch5 = new CountDownLatch(threadNum);
        for (int i = 0; i < threadNum; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000000000; j++) {
                    demo2.SingletonDemo instance = demo2.SingletonDemo.getInstance();
                }
                countDownLatch5.countDown();
            }).start();
        }
        countDownLatch5.await();
        end = System.currentTimeMillis();
        System.out.printf("枚举单例-%s个多线程各自创建1000000000次-耗时:%s%n", threadNum, end - start);
    }
}

执行结果

03-JAVA设计模式-单例模式详解,技术栈,# Java设计模式,java,设计模式,单例模式

如何选用

  • 单例对象占用资源少,不需要延时加载:枚举式 好于 饿汉式
  • 单例对象暂用资源大,需要延时加载:静态内部类 好于 懒汉式

通过反射方式破坏单例模式

验证

package demo7;

import demo4.SingletonDemo;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * 通过反射破坏单例模式
 *
 * @author Anna.
 * @date 2024/4/5 22:07
 */
public class TestClient {

    public static void main(String[] args) throws Exception {
        // 静态内部内实现单例模式
        SingletonDemo instance1 = SingletonDemo.getInstance();
        SingletonDemo instance2 = SingletonDemo.getInstance();

        System.out.printf("单线程模式下,判断获取的两个单例模式对象,判断是否是同一个对象:%s%n", instance1.hashCode() == instance2.hashCode());
        
        // 反射获取静态内部内实现单例模式
        Class<SingletonDemo> clazz = (Class<SingletonDemo>) Class.forName("demo4.SingletonDemo");
        // 获取构造器
        Constructor<SingletonDemo> declaredConstructor = clazz.getDeclaredConstructor(null);
        // 设置跳过安全检查
        declaredConstructor.setAccessible(true);
        // 初始化对象
        SingletonDemo singletonDemo1 = declaredConstructor.newInstance();
        SingletonDemo singletonDemo2 = declaredConstructor.newInstance();

        System.out.printf("反射方式,判断获取的两个单例模式对象,判断是否是同一个对象:%s%n", singletonDemo1.hashCode() == singletonDemo2.hashCode());
    }
}

执行结果:

03-JAVA设计模式-单例模式详解,技术栈,# Java设计模式,java,设计模式,单例模式

优化方案

SingletonDemo.java

package demo7;

public class SingletonDemo {

    // 1 定义一个静态内部类,内部类中提供一个静态final的单例对象属性
    static class SingletonDemoInstance{
        // 静态内部类 利用 JVM类加载器 加载类时 首先初始化 类中静态常量到常量池中且一个class只会被初始化一次这一特性保证线程安全
        private static final SingletonDemo instance = new SingletonDemo();
    }

    // 2 构造函数私有化
    private SingletonDemo() {
        // 反射通过调用SingletonDemo()进行初始化时,判断属性 SingletonDemoInstance.instance 是否为空,如果为空则直接抛出异常
        if(SingletonDemoInstance.instance != null){
            throw new RuntimeException();
        }
    }

    // 3 提供一个public静态的初始化方法getInstance()方法
    public static SingletonDemo getInstance() {
        return SingletonDemoInstance.instance;
    }
}

TestClient2.java

package demo7;

import java.lang.reflect.Constructor;

public class TestClient2 {

    public static void main(String[] args) throws Exception {
        // 静态内部内实现单例模式
        SingletonDemo instance1 = SingletonDemo.getInstance();
        SingletonDemo instance2 = SingletonDemo.getInstance();

        System.out.printf("单线程模式下,判断获取的两个单例模式对象,判断是否是同一个对象:%s%n", instance1.hashCode() == instance2.hashCode());
        
        // 反射获取静态内部内实现单例模式
        Class<SingletonDemo> clazz = (Class<SingletonDemo>) Class.forName("demo7.SingletonDemo");
        // 获取构造器
        Constructor<SingletonDemo> declaredConstructor = clazz.getDeclaredConstructor(null);
        // 设置跳过安全检查
        declaredConstructor.setAccessible(true);
        // 初始化对象
        SingletonDemo singletonDemo1 = declaredConstructor.newInstance();
        SingletonDemo singletonDemo2 = declaredConstructor.newInstance();

        System.out.printf("反射方式,判断获取的两个单例模式对象,判断是否是同一个对象:%s%n", singletonDemo1.hashCode() == singletonDemo2.hashCode());
    }
}

执行结果:

03-JAVA设计模式-单例模式详解,技术栈,# Java设计模式,java,设计模式,单例模式

通过反序列化方式破坏单例模式

序列化时java,必须实现Serializable接口,因此修改单例
SingletonDemo.java

package demo8;

import java.io.Serializable;

// 实现Serializable接口
public class SingletonDemo implements Serializable {

    // 序列化ID,用于版本控制  
    private static final long serialVersionUID = 1L;

    // 1 定义一个静态内部类,内部类中提供一个静态final的单例对象属性
    static class SingletonDemoInstance{
        // 静态内部类 利用 JVM类加载器 加载类时 首先初始化 类中静态常量到常量池中且一个class只会被初始化一次这一特性保证线程安全
        private static final SingletonDemo instance = new SingletonDemo();
    }

    // 2 构造函数私有化
    private SingletonDemo() {
    }

    // 3 提供一个public静态的初始化方法getInstance()方法
    public static SingletonDemo getInstance() {
        return SingletonDemoInstance.instance;
    }
}

TestClient.java

package demo8;

import demo8.SingletonDemo;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

/**
 * 通过反序列化单例模式
 *
 * @author Anna.
 * @date 2024/4/5 22:07
 */
public class TestClient {

    public static void main(String[] args) throws Exception {
        // 静态内部内实现单例模式
        SingletonDemo instance1 = SingletonDemo.getInstance();
        SingletonDemo instance2 = SingletonDemo.getInstance();

        System.out.printf("单线程模式下,判断获取的两个单例模式对象,判断是否是同一个对象:%s%n", instance1.hashCode() == instance2.hashCode());
        
        // 序列化instance1到本地文件中 序列化时必须实现序列化接口
        try(FileOutputStream fos = new FileOutputStream("D:/temp/a.txt"); ObjectOutputStream oos = new ObjectOutputStream(fos);){
            oos.writeObject(instance1);
        }

        // 反序列化获取instance1对象
        try(FileInputStream fis = new FileInputStream("D:/temp/a.txt"); ObjectInputStream ois = new ObjectInputStream(fis);){
            SingletonDemo instance3 = (SingletonDemo) ois.readObject();
            System.out.printf("反序列化方式,判断获取的两个单例模式对象,判断是否是同一个对象:%s%n", instance1.hashCode() == instance3.hashCode());
        }
    }
}

执行结果:

03-JAVA设计模式-单例模式详解,技术栈,# Java设计模式,java,设计模式,单例模式

优化方案

为了避免当通过反序列化一个单例类的实例时,可能会创建该类的另一个实例,从而破坏单例模式的唯一性保证。

可以通过在单例类中实现readResolve方法来确保反序列化时返回的是单例的原始实例。

readResolve方法是java.io.Serializable接口中的一个特殊方法, 当反序列化对象时,如果对象所属的类定义了readResolve方法, JVM会调用这个方法,并返回其返回的对象,而不是新创建的对象。

因此,我们可以在单例类的readResolve方法中返回单例的原始实例,以确保反序列化不会创建新的实例。

package demo8;

import java.io.ObjectStreamException;
import java.io.Serializable;

// 实现Serializable接口
public class SingletonDemo implements Serializable {

    // 序列化ID,用于版本控制
    private static final long serialVersionUID = 1L;

    // 1 定义一个静态内部类,内部类中提供一个静态final的单例对象属性
    static class SingletonDemoInstance{
        // 静态内部类 利用 JVM类加载器 加载类时 首先初始化 类中静态常量到常量池中且一个class只会被初始化一次这一特性保证线程安全
        private static final SingletonDemo instance = new SingletonDemo();
    }

    // 2 构造函数私有化
    private SingletonDemo() {
    }

    // 3 提供一个public静态的初始化方法getInstance()方法
    public static SingletonDemo getInstance() {
        return SingletonDemoInstance.instance;
    }

    // readResolve方法是java.io.Serializable接口中的一个特殊方法,
    // 当反序列化对象时,如果对象所属的类定义了readResolve方法,
    // JVM会调用这个方法,并返回其返回的对象,而不是新创建的对象。
    // 因此,我们可以在单例类的readResolve方法中返回单例的原始实例,以确保反序列化不会创建新的实例。
    protected Object readResolve() throws ObjectStreamException {
        return getInstance();
    }
}

执行结果:

03-JAVA设计模式-单例模式详解,技术栈,# Java设计模式,java,设计模式,单例模式

gitee源码

git clone https://gitee.com/dchh/JavaStudyWorkSpaces.git文章来源地址https://www.toymoban.com/news/detail-849484.html

到了这里,关于03-JAVA设计模式-单例模式详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【java】设计模式——单例模式

    单例模式要点 : 一个类只需要一个实例化对象; 必须自行创建实例; 必须自行向整个系统提供这个实例 实现 : 只提供 私有 构造方法; 有一个该类的 静态 私有对象; 提供一个静态 公有 方法用于创建、获取静态私有对象; 分析: 私有构造方法-不能随意创建实例; 静态

    2024年02月13日
    浏览(47)
  • JAVA设计模式——单例模式

    单例模式是应用最广的设计模式之一,也是程序员最熟悉的一个设计模式,使用单例模式的类必须保证只能有创建一个对象。 今天主要是回顾一下单例模式,主要是想搞懂以下几个问题 为什么要使用单例? 如何实现一个单例? 单例存在哪些问题? 单例对象的作用域的范围

    2024年02月16日
    浏览(44)
  • Java 设计模式——单例模式

    (1)单例模式 (Singleton Pattern) 是 Java 中最简单的设计模式之一。它提供了一种创建对象的最佳方式。 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的

    2024年02月13日
    浏览(61)
  • Java设计模式【单例模式】

    单例模式(Singleton Pattern)是一种创建型设计模式,其主要目的是确保一个类只有一个实例,并提供对该实例的唯一访问点。 优点 : 提供了对唯一实例的受控访问。 由于在系统内存中只存在一个对象,因此可以节约系统资源。 缺点 : 单例类的扩展有很大的困难。 单例类的

    2024年02月04日
    浏览(62)
  • java设计模式-单例

    单例模式是一种创建型设计模式,它可以保证一个类只有一个实例,并提供全局访问点。单例模式在实际开发中经常使用,可以避免多个实例引起的资源浪费和同步问题。常见的java实现方式有多种。 饿汉式单例模式是指在类加载时就创建了单例对象,因此在调用时不需要再

    2024年01月18日
    浏览(53)
  • Java设计模式---单例 工厂 代理模式

    单例模式是设计模式中的一种,属于创建型模式。在软件工程中,单例模式确保一个类只有一个实例,并提供一个全局访问点。这种模式常用于那些需要频繁实例化然后引用,且创建新实例的开销较大的类,例如数据库连接池、缓存管理等。 意图 :保证一个类仅有一个实例

    2024年01月24日
    浏览(52)
  • 设计模式篇(Java):单例模式

    上一篇:设计模式篇(Java):前言(UML类图、七大原则) 所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。 构造器私有化 (防止 new ) 类的内部创建对象 向外暴露一个静

    2024年02月11日
    浏览(61)
  • Java——单例设计模式

    设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式免去我们自己再思考和摸索。就像是经典的棋谱,不同的棋局,我们用不同的棋谱、“套路”。 经典的设计模式共有23种。每个设计模式均是特定环境下特定问题的

    2024年02月11日
    浏览(47)
  • 【JAVA】Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式

    🍎 个人博客: 个人主页 🏆 个人专栏: JAVA ⛳️   功不唐捐,玉汝于成 目录 前言 正文 懒汉式(Lazy Initialization): 双重检查锁定(Double-Checked Locking): 结语 我的其他博客 在软件设计中,单例设计模式是一种重要的设计思想,它确保了一个类只有一个实例,并提供了一

    2024年01月15日
    浏览(55)
  • Java设计模式(八)— 单例模式3

    单例模式之静态内部类 单例模式之枚举方式 单例模式之JDK源码分析 Hello,小伙伴们,欢迎来到柚子的博客~让我们一起成长吧o( ̄▽ ̄)ブ 提示:以下是本篇文章正文内容,下面案例可供参考 代码如下(示例): 类被装载的时候,类里面的静态内部类也是会被装载的,而且线

    2024年02月09日
    浏览(76)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包