标签
代码见:
📝 定义
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
- 私有化构造函数
- 创建对象逻辑自行控制,且仅允许创建一个
📝 应用
优点
- 全局访问点:单例模式提供了一个全局访问点,方便访问该类的实例,这样可以根据需要在整个应用程序中共享状态。例如可以设计一个单例类,负责所有数据表的映射处理。
- 控制实例数量:有效控制类的实例数量,避免了资源的不必要浪费。
- 懒加载:可以实现懒加载,只有在需要的时候才创建该实例,有助于提高应用的性能。
- 避免重复实例化:单例模式避免了多次创建对象的开销,降低了内存的使用。(TODO:需补充为何创建销毁无法优化性能 )
- 方便管理共享资源:在需要共享的资源场景中(如数据库连接池、线程池等),单例模式确保统一管理,避免资源冲突。
缺点
- 难以测试:单例模式可能导致代码难以测试,因为其状态是全局的。单元测试中可能需要清理状态,这使得测试变得更复杂。
- 使用不当可能导致资源泄露:如果单例持有大量资源(如数据库连接、文件句柄等),并且在不需要时不释放,可能会导致资源泄露。
- 并发问题:在多线程环境下,单例的实现需要小心,确保只有一个实例被创建,否则可能会导致多个实例被创建(即在高并发情况下不安全)。
- 增加依赖性:使用单例会增加类与单例类之间的依赖性,从而降低了系统的可变性和灵活性。
使用场景
- 要求生成唯一序列号的环境。
- 在整个项目中需要一个共享访问点或共享数据,例如一个Web页面上的计数器,可以不用把每次刷新都记录到数据库中,使用单例模式保持计数器的值,并确保是线程安全的。
- 创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源。
- 需要定义大量的静态常量和静态方法(如工具类)的环境,可以采用单例模式(当然,也可以直接声明为static的方式)。
- 池:如数据库连接池、线程池等,优化线程/连接等管理,使得整个应用中相同业务属性的只有一个池实例,以免冗余开销
注意事项
使用单例模式时,要考虑线程安全、资源管理、可维护性等多方面的问题,以实现其预期效果,并避免引入不必要的复杂性和Bug。精心设计和文档化单例类能够提高代码的质量和可读性。
- 线程安全:在多线程环境中,确保单例实例的创建是线程安全的。可以使用双重检查锁定或其他线程同步机制(如
volatile
关键字)来避免多个线程同时创建实例。
- 避免过度使用:不要在不需要的情况下使用单例模式。过度使用单例可能导致代码耦合,影响可维护性。考虑是否可以使用依赖注入或其他设计模式。
- 资源管理:确保单例类管理的资源(如数据库连接、文件句柄等)能被正确释放。避免在应用程序结束时仍然占用资源。
- 序列化问题:如果单例类需要序列化,确保在反序列化时不会创建新的实例。通常可以通过实现
readResolve
方法来解决这个问题。
- 隐藏构造器:单例类的构造函数应该是私有的,以防止外部类创建多个实例。
- 单例类的生命周期:了解单例对象的生命周期,以免在不需要的时候长时间占用内存或资源。不应无休止地保留单例实例。
- 与依赖注入结合:在需要灵活性的项目中,可以考虑将单例的使用与依赖注入框架结合,这样可以提高可测试性和可维护性。
- 监测对象的状态:有时单例对象会维持某些状态。如果这个状态不应该被多个模块共享,应该考虑其他设计模式。
- 代码文档:明确标注单例类的用途及其限制,确保后续维护人员理解该类的设计意图和使用要求。
- 特殊情况处理:如果需要支持某些特殊情况(如动态替换单例实例),可以设计成允许重新替换实例的单例模式,但这通常要小心使用。
📝 扩展
📎 最佳实践
- 一些引用
- 引用文章
有关Notion安装或者使用上的问题,欢迎您在底部评论区留言,一起交流~