网站首页 > java教程 正文
单例模式指的是一个类,在全局范围内(整个系统中)有且只能有一个实例存在。即该类本身负责提供一种访问其唯一对象的方式,不对外提供公共的构造函数(禁用默认公共构造函数),对于该类的实例化由它自己在类的内部进行维护。
Ensure a class has only one instance, and provide a global point of access to it.(确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。)
Singleton类称为单例类,通过使用private的构造函数确保了在一个应用中只产生一个实例,并且是自行实例化的(在Singleton中自己使用new Singleton())。
单例模式通用代码:
public class Singleton {
private static final Singleton singleton = new Singleton();
//禁止外部通过new 的方式生成对象
private Singleton(){}
//通过该方法获得实例对象
public static Singleton getSingleton(){
//确认当前系统中不存在该对象
if(singleton == null){
singleton = new Singleton();
}
//返回单例实体
return singleton;
}
//类中其他方法,尽量是static
public static void doSomething(){}
}
单例模式优缺点:
优点
● 由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显。
● 由于单例模式只生成一个实例,所以减少了系统的性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后用永久驻留内存的方式来解决(在Java EE中采用单例模式时需要注意JVM垃圾回收机制)。
● 单例模式可以避免对资源的多重占用,例如一个写文件动作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作。
● 单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理。
缺点
● 单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本上没有第二种途径可以实现。单例模式为什么不能增加接口呢?因为接口对单例模式是没有任何意义的,它要求“自行实例化”,并且提供单一实例、接口或抽象类是不可能被实例化的。当然,在特殊情况下,单例模式可以实现接口、被继承等,需要在系统开发中根据环境判断。
● 单例模式对测试是不利的。在并行开发环境中,如果单例模式没有完成,是不能进行测试的,没有接口也不能使用mock的方式虚拟一个对象。
● 单例模式与单一职责原则有冲突。一个类应该只实现一个逻辑,而不关心它是否是单例的,是不是要单例取决于环境,单例模式把“要单例”和业务逻辑融合在一个类中。
单例模式的实用场景
在一个系统中,要求一个类有且仅有一个对象,如果出现多个对象就会出现“不良反应”,可以采用单例模式,具体的场景如下:
● 要求生成唯一序列号的环境;
● 在整个项目中需要一个共享访问点或共享数据,例如一个Web页面上的计数器,可以不用把每次刷新都记录到数据库中,使用单例模式保持计数器的值,并确保是线程安全的;
● 创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源;
● 需要定义大量的静态常量和静态方法(如工具类)的环境,可以采用单例模式(当然,也可以直接声明为static的方式)。
单例模式需要注意的两个地方:线程安全和对象复制。
线程安全问题,在高并发情况下创建单例的过程中,可以根据单例模式的基础代码可以看出来在返回对象之前先会判断这个对象是否已经被创建,当并发发生在return之前,判断条件(singleton == null)的值是true,这样就会生成两个对象,所以是线程不安全的。解决办法,可以在getSingleton方法前加synchronized关键字,也可以在getSingleton方法内增加synchronized来实现。
对象的复制情况。在Java中,如果调用Cloneable接口,并实现了clone方法,则可以直接通过对象复制方式创建一个新对象,对象复制是不用调用类的构造函数,因此即使是私有的构造函数,对象仍然可以被复制。解决该问题的最好方法就是单例类不要实现Cloneable接口,单例模式类复制的实现场景也是极少的。
单例模式也是最常见的一种设计模式,spring框架中Bean的管理默认是采用单例模式,由spring框架管理Bean的生命周期。
单例模式的实用方式
1、枚举(线程安全)
这是实现单例模式的最佳方法,更加简洁,自动支持序列化,杜绝防止多次实例化,非常高效!(强烈推荐使用)
public enum SingletonPattern {
INSTANCE;
public static SingletonPattern getInstance(){
return INSTANCE;
}
}
2、内部静态类(线程安全)
这种方式可以达到跟双重校验锁一样的效果,但只适用于静态域的情况,双重校验锁可在实例域需要延迟初始化时使用。
public class SingletonPattern {
private static class SingletonPatternHolder{
public static SingletonPattern instance = new SingletonPattern();
}
private SingletonPattern(){}
public static SingletonPattern getInstance(){
return SingletonPatternHolder.instance;
}
}
3、饿汉模式(线程安全)
该方式虽然简单也安全,但是会造成再不需要实例时,产生垃圾对象,造成资源浪费,因此,一般不使用。
public class SingletonPattern {
private static SingletonPattern instance = new SingletonPattern();
private SingletonPattern(){}
public static SingletonPattern getInstance(){
return instance;
}
}
4、懒加载(线程不安全)
以上方式,如果存在多个线程同时访问getInstance()时,由于没有锁机制,会导致实例化出现两个实例的情况,因此,在多线程环境下时不安全的。
public class SingletonPattern {
private static SingletonPattern instance;
private SingletonPattern(){}
public static SingletonPattern getInstance(){
if (instance == null){
instance = new SingletonPattern();
}
return instance;
}
}
5、懒加载(线程安全)
在getInstance()方法上添加了同步锁。但是该方法虽然解决了线程安全的问题,但却也带来了另外的一个问题,就是每次获取对象时,都要先获取锁,并发性能很差,还需要继续优化!
public class SingletonPattern {
private static SingletonPattern instance;
private SingletonPattern(){}
public static synchronized SingletonPattern getInstance(){
if (instance == null){
instance = new SingletonPattern();
}
return instance;
}
}
6、双重校验(线程安全)
该方法将方法上的锁去掉了,避免了每次调用该方法都要获取锁的操作,从而提升了并发性能,同时在方法内部使用锁,进而解决了并发的问题,从而解决了上面并发安全+性能低效的问题,是个不错的实现单例的方式。
public class SingletonPattern {
private static SingletonPattern instance;
private SingletonPattern(){}
public static SingletonPattern getInstance(){
if (instance == null){
synchronized (SingletonPattern.class){
instance = new SingletonPattern();
}
}
return instance;
}
}
关于单例模式,不仅要懂得它的实现形式,也是需要学会手写单例的,很多公司在面试过程中很喜欢面试提问一些类似的问题,虽然在工作中需要自己注意单例创建的场景并不是很多,学会还是很有必要的。
猜你喜欢
- 2024-09-22 带你了解单例模式(Singleton Pattern)的五种写法!
- 2024-09-22 单例模式的常用写法(单例模式三种写法)
- 2024-09-22 GoF之单例模式详解(单例模式在哪里使用)
- 2024-09-22 设计模式之-单例模式(单例设计模式的作用)
- 2024-09-22 Java(面试)——单例模式(java中单例模式的实现)
- 2024-09-22 在Java中实现单例模式(java单例模式实现方式)
- 2024-09-22 Java单例模式(java单例模式双重检查)
- 2024-09-22 Java设计模式之单例模式解析(java 单例模式 例子)
- 2024-09-22 本文将给大家介绍java中设计模式——单例模式
- 2024-09-22 java面试:单例模式,一篇就够了(java单例模式写法)
你 发表评论:
欢迎- 最近发表
-
- class版本不兼容错误原因分析(class更新)
- 甲骨文Oracle公司为Java的最新LTS版本做出改进
- 「版本发布」Minecraft Java开发版 1.19.4-pre1 发布
- java svn版本管理工具(svn软件版本管理)
- 我的世界1.8.10钻石在第几层(我的世界1.7.2钻石在哪层)
- Java开发高手必备:在电脑上轻松切换多个JDK版本
- 2022 年 Java 开发报告:Java 8 八年不到,开发者都在用什么?
- 开发java项目,选择哪个版本的JDK比较合适?
- Java版本选型终极指南:8 vs 17 vs 21特性对决!大龄程序员踩坑总结
- POI Excel导入(poi excel导入附件)
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)