单例模式
1. 单例特点:
- 单例类只有一个实例对象
- 该单例对象必须有单例类自行创建
- 单例类对外提供一个访问该单例的全局访问点
2. 实现方式
- 饿汉式
优点: 线程安全,在类加载时创建一个实例,调用反应速度快。
缺点: 资源利用率不高,可能getInstance()永远执行不到,但是调用该类的其他静态方法或者加载该类,仍然会实例化对象,浪费空间。
代码示例:
//饿汉式
public class HungrySingleton {
//私有静态单例对象
private static HungrySingleton singleton = new HungrySingleton();
//提供公共静态方法,以供外部访问
public static HungrySingleton getInstance(){
return singleton;
}
//构造方法私有,避免外部new实例
private HungrySingleton() {
}
}
- 懒汉式
优点: 延迟加载,不浪费空间
缺点: 线程不安全
代码示例:
public class LazySingleTon {
//私有静态单例对象
private static LazySingleTon singleTon;
//提供公共静态方法,以供外部访问
public static LazySingleTon getInstance(){
if(singleTon == null){
singleTon = new LazySingleTon();
}
return singleTon;
}
//构造方法私有,避免外部new实例
private LazySingleTon(){
}
}
- 双重校验锁
优点: 延迟加载,线程安全
缺点: 第一次使用加载不够快,多线程调用不必要的同步开销大
代码示例:
public class DoubleCheckSingleTon {
//私有静态单例对象
private static DoubleCheckSingleTon singleTon;
//提供公共静态方法,以供外部访问
public static DoubleCheckSingleTon getInstance(){
if(singleTon == null){
synchronized (DoubleCheckSingleTon.class){
if(singleTon == null){
singleTon = new DoubleCheckSingleTon();
}
}
}
return singleTon;
}
//构造方法私有,避免外部new实例
private DoubleCheckSingleTon() {
}
}
- 静态内部类
优点: 线程安全,在类加载时创建一个实例,效率高
缺点: 资源利用率不高,可能getInstance()永远执行不到,但是调用该类的其他静态方法或者加载该类,仍然会实例化对象,浪费空间。
代码示例:
public class StaticSingleTon {
//静态内部类
private static class Inner{
//私有静态单例对象
private static StaticSingleTon SINGLETON = new StaticSingleTon();
}
//提供公共静态方法,以供外部访问
private static StaticSingleTon singleTon(){
return Inner.SINGLETON;
}
//构造方法私有,避免外部new实例
private StaticSingleTon() {
}
}
- 枚举(推荐)
优点: 天然线程安全,无需锁,简洁,反射/序列化不会破坏单例
缺点: 不可继承,不能延迟加载
代码示例:
public class EnumSingleTon {
//枚举类
private static enum Singleton{
SINGLETON;
//私有静态单例对象
private final EnumSingleTon singleTon;
private Singleton(){
singleTon = new EnumSingleTon();
}
private EnumSingleTon getInstance(){
return singleTon;
}
}
//提供公共静态方法,以供外部访问
public static EnumSingleTon getInstance(){
return Singleton.SINGLETON.singleTon;
}
//构造方法私有,避免外部new实例
private EnumSingleTon() {
}
}
防止单例被破坏
1. 反射
解决方案:在构造方法中加判断
示例:
private TestDestory(){
//防止反射破坏单例
if(singleTon != null){
throw new RuntimeException("此举会破坏单例!!!");
}
}
2. 序列化
解决方案:单例类中添加readResolve方法
示例:
//防止序列化破坏单例
private Object readResolve(){
return singleTon;
}
3. 克隆
解决方案:重写克隆方法,直接返回单例对象
示例:
//防止克隆破坏单例
@Override
protected Object clone() throws CloneNotSupportedException{
return singleTon;
}
参考: https://www.kaikeba.com/open/item?lpsc=249750