Effective Java -- 构造单例(Singleton)对象常用方法

此系列文章为本人对《Effective Java》一书的学习笔记,主要是记录对书中重点内容的理解。
既然有缘看到此文,那么希望能对你有所帮助。
本文对应原书 第3条 用私有构造器或枚举类型强化Singleton属性

Singleton是指仅被实例化一次的类,也就是单例。通常被用来代表一个无状态的对象(如函数)和系统组件。实现单例的常见的有以下两种方法:

公有静态域和静态工厂方法
公有静态域
public class Demo {
	// 公有静态final域
	public static final Demo INSTANCE = new Demo();
	// 私有构造方法
	private Demo() {
		...
	}
}

私有构造器仅在初始化INSTANCE的时候被调用一次,由于外界无法直接访问该构造器,所以一但Demo类被实例化,只会存在一个Demo示例,这就保证了单例;

但是,事情往往没有想象的那么简单,通过Java的反射机制,使用setAccessible方法,可以随意调用这个私有构造器。

所以我们还要加上一些判断逻辑,使得在该构造器被调用第二次的时候抛出异常。

public class Demo {
	private static final boolean Flag = false;
	public static final Demo INSTANCE = new Demo();
	private Demo() {
		if (flag) {
			throw new RuntimeException("不许使用骚操作");
		}
		flag = true...
	}
}

优点:

  • 清晰的表明了这个类是Singleton
  • 非常简单
静态工厂方法
public class Demo {
	// 注意这里是private域
	private static final Demo INSTANCE = new Demo();
	// 私有构造方法
	private Demo() {...}
	public static Demo instance() {
		return INSTANCE;
	}
}

其实和上一个方法区别不大,原理一致,所以也得处理反射导致的私有构造器被非法调用的问题。相比之下,静态工厂方法有以下几个优势,

  • 更加灵活:比如我们需要的并不是全局的单例,而是伴随线程的生命周期,在一个线程内使用同一个对象,不与其他线程共用(Spring就有有关于单例生命周期的设置项);
  • 支持泛型:在需要的情况下可以写一个泛型Singleton工厂;
  • 支持方法引用:通过方法引用:Demo::instance成为一个提供者Supplier<Demo>

但如果这三点优势都不是你所关注的,那么静态工厂方法实质上并不如公有静态域来的实在。

关于序列化的问题(重点内容)
我们都知道想要实现序列化只要实现 Serializable 接口即可。但对于需要实现单例的类来说还不够。
因为每一次反序列化,都会创健一个新的实例,也就破坏了我们苦心经营的单例。
解决办法:在类中加入一个readResolve方法(请仔细阅读注释)。

public class Demo implements Serializable {
	...

	// 注意这不是一个需要重写的方法(Serializable接口并没有抽象方法)
	// 这个方法会在反序列化时被调用
	// 返回了的静态变量INSTANCE,也就是我们要的单例(静态变量不属于一个类,它属于整个世界)
	// 垃圾处理器会处理掉反序列化而产生的其他实例对象
	private Object readResolve() {
		return INSTANCE;
	}
}
枚举方法

使用枚举其实和上述的公有静态域方法相似,但是它更加简洁提供了序列化机制绝对防止多次实例化(地表最强防御)

public enum Demo {
	INSTANCE;
	...
}

虽然这种方法现在似乎不常见,但这种单元素的枚举类型经常成为实现单例的最佳方法

当然也有例外的情况:如果你需要创健子类来继承Demo,这个方法会显得不太便捷,不建议使用

总结

当你需要用单例时,不要忘了考虑枚举,它真的很努力,很Nice。

水平有限,若文章中存在错误,恳请不吝赐教,这对我以及后面的读者都有重要意义;
若文章能够帮助到你,还望一键三连,你的支持,是我最大的动力。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值