Java设计模式之单例模式详解(懒汉式和饿汉式)

在开发工作中,有些类只需要存在一个实例,这时就可以使用单例模式。Java中的单例模式是一种常见的设计模式,它确保一个类只有一个实例,并提供全局访问点。下面来介绍一下两种常见的单例模式:懒汉式和饿汉式。

一、懒汉式

懒汉式属于一种延迟加载的单例模式,它的特点是在第一次使用时创建实例对象,而不是在类加载时就创建。

1.1 代码示例

java 复制代码
public class LazySingleton {
    private static LazySingleton instance;
    
    private LazySingleton() {
        // 私有构造方法
    }
    
    public static synchronized LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

在代码中,我把构造函数声明为私有的,以防止其他类直接通过构造函数创建实例对象。
getInstance() 方法是获取实例对象的入口。在该方法内部,首先检查实例对象 instance是否已经被创建。如果实例对象为 null,表示还没有创建过,就通过调用构造函数来创建实例对象。如果已经创建过,就直接返回已有的实例对象。

为了保证线程安全,在 getInstance() 方法上添加了 synchronized 关键字,使得在多线程环境下只有一个线程能够进入创建实例。保证了多线程情况下只会创建一个实例对象。

1.2 适用场景

  1. 实例的创建和初始化需要消耗较多时间或资源时,不希望在程序启动时就加载实例。
  2. 需要延迟加载实例,只有在需要的时候才创建。
  3. 需要在不同的线程中使用单例对象。

懒汉式可以避免在不需要实例对象时的资源浪费,只有在需要时才进行创建。这种延迟加载的特性使得它在某些情况下更加高效。

下面举一个管理文件的读写操作的例子:

java 复制代码
public class FileManager {
    private static FileManager instance;
    private File file;

    private FileManager() {
        // 初始化文件对象
        file = new File("D:\\file\\demo.txt");
    }

    public static synchronized FileManager getInstance() {
        if (instance == null) {
            instance = new FileManager();
        }
        return instance;
    }

    public void readFile() {
        // 读取文件内容并进行相应操作...
    }

}

在上述示例中,通过懒汉式单例模式,我们可以确保在第一次调用 getInstance() 方法时才创建实例对象。其他类可以通过调用该方法获取 FileManager 的实例对象。然后,可以使用该实例对象的方法来进行文件操作,例如 readFile() 方法。这样,无论在哪个地方需要读写文件,都可以通过 FileManager.getInstance().readFile() 方法来使用文件管理器。

总的来说,懒汉式适用于在第一次使用时才进行对象创建的场景,并且在实例对象初始化过程中没有复杂的线程安全要求。文件管理器就是一个典型的例子,因为在应用程序启动时可能不需要立即读写文件,而是在需要的时候才进行相关操作。

1.3 注意事项

  1. 线程安全:懒汉式是线程安全的,因为通过给 getInstance() 方法添加 synchronized 关键字,可以保证在多线程环境下只有一个线程能够进入创建实例。但是这也带来了性能上的开销。
  2. 性能开销:由于加了锁,每次获取实例都需要进行同步控制,可能会引起一定的性能问题。如果对性能要求较高,可以考虑使用双重检查锁定(Double-Checked Locking)等方式进行改进,以减少同步开销。
  3. 可序列化:如果需要将懒汉式单例对象序列化到文件或网络中,需要注意实现 Serializable 接口,以确保对象的序列化和反序列化过程正确无误。

二、饿汉式

饿汉式是一种在类加载时就创建实例的单例模式。它的特点是无论是否会被使用到,实例对象都在类加载时被创建。

2.1 示例代码

java 复制代码
public class EagerSingleton {
    private static final EagerSingleton instance = new EagerSingleton();
    
    private EagerSingleton() {
        // 私有构造方法
    }
    
    public static EagerSingleton getInstance() {
        return instance;
    }
}

在代码中,先把实例对象 instance 定义为静态的 final 变量,并在声明时就进行实例化。

因为实例对象在类加载时就被创建,所以可以保证在任何情况下都能获取到同一个实例对象。

2.2 适用场景

  1. 实例的创建和初始化过程较为简单,并且不会消耗过多的时间或资源。
  2. 希望在程序启动时就加载实例,避免在后续代码中频繁创建和初始化实例。
  3. 饿汉式能够保证在任何时候都能获取到实例对象,适用于简单的单例对象的创建和初始化。

下面举一个管理日志的例子:

java 复制代码
public class Logger {
    private static final Logger instance = new Logger();
    
    private Logger() {
        // 初始化日志记录器
    }
    
    public static Logger getInstance() {
        return instance;
    }
    
    public void log(String message) {
        // 记录日志消息
    }
    
    // 其他日志操作方法...
}

上述示例中,在 Logger 类的内部,将实例对象 instance 定义为静态的 final 变量,并在声明时就进行实例化。这样,在类加载时实例对象就会被创建。然后通过提供 getInstance() 方法,其他类可以调用该方法获取 Logger 的实例对象。然后,可以使用该实例对象的方法来记录日志消息,例如 log(String message) 方法。

这样,无论在哪个地方需要记录日志,都可以通过 Logger.getInstance().log(String message) 方法来使用日志记录器。

总的来说,饿汉式适用于需要在程序启动时就初始化的资源,且在整个应用程序的生命周期中都需要使用到的场景。日志记录器就比较适合,因为日志记录功能通常需要在应用程序启动之初就准备好,并在整个应用程序的运行过程中记录日志消息。

2.3 注意事项

  1. 线程安全:饿汉式是线程安全的,因为实例对象已经在类加载时就创建好了,不存在多线程环境下的竞争问题。
  2. 性能与资源消耗:由于实例对象在类加载时就被创建,可能会导致一些性能和资源上的浪费,特别是在某些情况下实例对象并没有被使用到。

饿汉式是一种简单且直接的方式来创建单例对象,但也可能带来一些不必要的性能和资源消耗。因此,在实际应用中需要根据具体需求进行权衡和选择。

三、总结

懒汉式和饿汉式是两种常见的Java单例模式。懒汉式在第一次使用时创建实例,而饿汉式在类加载时就创建实例。需要注意的是,在实际应用中,应根据具体场景进行综合考虑和设计。选择合适的单例模式可以提高代码的可维护性和性能 ~

相关推荐
收破烂的小熊猫~5 分钟前
《Java修仙传:从凡胎到码帝》第四章:设计模式破万法
java·开发语言·设计模式
猴哥源码14 分钟前
基于Java+SpringBoot的动物领养平台
java·spring boot
老任与码18 分钟前
Spring AI Alibaba(1)——基本使用
java·人工智能·后端·springaialibaba
小兵张健19 分钟前
武汉拿下 23k offer 经历
java·面试·ai编程
FreeBuf_29 分钟前
Apache组件遭大规模攻击:Tomcat与Camel高危RCE漏洞引发数千次利用尝试
java·tomcat·apache
无妄-202440 分钟前
软件架构升级中的“隐形地雷”:版本选型与依赖链风险
java·服务器·网络·经验分享
qqxhb44 分钟前
零基础数据结构与算法——第四章:基础算法-排序(上)
java·数据结构·算法·冒泡·插入·选择
猴哥源码1 小时前
基于Java+SpringBoot的在线小说阅读平台
java·spring boot
lingRJ7771 小时前
从混沌到掌控:基于OpenTelemetry与Prometheus构建分布式调用链监控告警体系
java·springboot·prometheus·backend·opentelemetry·jaeger·microservices
星辰离彬1 小时前
Java 与 MySQL 性能优化:Java应用中MySQL慢SQL诊断与优化实战
java·后端·sql·mysql·性能优化