在Java中实现单例设计模式的有效方法是什么?
当前回答
在写它之前,真的要考虑一下为什么你需要一个单例。关于使用它们有一个准宗教的争论,如果你在Java中搜索单例,你很容易就会发现。
就我个人而言,出于许多原因,我尽量避免单身汉,其中大部分可以通过谷歌搜索单身汉找到。我觉得单身汉经常被虐待,因为他们很容易被每个人理解。它们被用作将“全局”数据引入OO设计的机制,因为它很容易绕过对象生命周期管理(或者真正思考如何从B内部实现a)。看看像控制反转(IoC)或依赖注入(DI)这样的事情,可以找到一个不错的中间地带。
如果你真的需要一个,那么Wikipedia有一个很好的例子来说明单例的正确实现。
其他回答
确保你真的需要它。在谷歌上搜索“singleton反模式”,看看反对它的一些论点。
我想这并没有什么本质上的问题,但它只是一种公开某些全局资源/数据的机制,因此请确保这是最好的方法。特别是,我发现依赖注入(DI)更有用,特别是如果您也在使用单元测试,因为DI允许您将模拟资源用于测试目的。
如果需要惰性地加载类的实例变量,则需要双重检查习惯用法。如果需要惰性地加载静态变量或单例,则需要按需初始化持有者习惯用法。
此外,如果单例需要可序列化,则所有其他字段都需要是暂时的,并且需要实现readResolve()方法以保持单例对象不变。否则,每次反序列化对象时,都会创建对象的新实例。readResolve()所做的是用readObject()替换读取的新对象,因为没有变量引用该对象,所以强制对该新对象进行垃圾回收。
public static final INSTANCE == ....
private Object readResolve() {
return INSTANCE; // Original singleton instance.
}
看看这篇文章。
Java核心库中的GoF设计模式示例
从最佳答案的“Singleton”部分,
Singleton(可由每次返回相同实例(通常是其本身)的创建方法识别)java.lang.Runtime#获取运行时间()java.awt.Desktop#getDesktop()java.lang.System#getSecurityManager()
您还可以从Java本机类本身学习Singleton的示例。
枚举单例
实现线程安全的单例的最简单方法是使用Enum:
public enum SingletonEnum {
INSTANCE;
public void doSomething(){
System.out.println("This is a singleton");
}
}
自从在Java 1.5中引入Enum以来,此代码一直有效
双重检查锁定
如果你想编写一个在多线程环境中工作的“经典”单例(从Java1.5开始),你应该使用这个。
public class Singleton {
private static volatile Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class){
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
这在1.5之前不是线程安全的,因为volatile关键字的实现不同。
早期加载单例(甚至在Java1.5之前就可以使用)
这个实现在加载类时实例化单例,并提供线程安全性。
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
public void doSomething(){
System.out.println("This is a singleton");
}
}
另一个经常用来反对单态的论点是它们的可测试性问题。出于测试目的,单体不容易被模拟。如果这是一个问题,我想做以下轻微修改:
public class SingletonImpl {
private static SingletonImpl instance;
public static SingletonImpl getInstance() {
if (instance == null) {
instance = new SingletonImpl();
}
return instance;
}
public static void setInstance(SingletonImpl impl) {
instance = impl;
}
public void a() {
System.out.println("Default Method");
}
}
添加的setInstance方法允许在测试期间设置单例类的实体模型实现:
public class SingletonMock extends SingletonImpl {
@Override
public void a() {
System.out.println("Mock Method");
}
}
这也适用于早期初始化方法:
public class SingletonImpl {
private static final SingletonImpl instance = new SingletonImpl();
private static SingletonImpl alt;
public static void setInstance(SingletonImpl inst) {
alt = inst;
}
public static SingletonImpl getInstance() {
if (alt != null) {
return alt;
}
return instance;
}
public void a() {
System.out.println("Default Method");
}
}
public class SingletonMock extends SingletonImpl {
@Override
public void a() {
System.out.println("Mock Method");
}
}
这有一个缺点,就是将此功能也暴露给普通应用程序。其他编写该代码的开发人员可能会尝试使用“setInstance”方法来更改特定函数,从而改变整个应用程序的行为,因此该方法的javadoc中至少应该包含一个良好的警告。
尽管如此,对于模型测试的可能性(当需要时),这种代码暴露可能是可以接受的代价。
推荐文章
- Eclipse调试器总是阻塞在ThreadPoolExecutor上,没有任何明显的异常,为什么?
- Java生成两个给定值之间的随机数
- 如何有效地从数组列表或字符串数组中删除所有空元素?
- 比较JUnit断言中的数组,简洁的内置方式?
- codestyle;把javadoc放在注释之前还是之后?
- 如何在Spring中定义List bean ?
- 将Set<T>转换为List<T>的最简洁的方法
- 在JavaScript中,什么相当于Java的Thread.sleep() ?
- 使用Java重命名文件
- URL从Java中的类路径加载资源
- .toArray(new MyClass[0]) or .toArray(new MyClass[myList.size()])?
- Hibernate中不同的保存方法之间有什么区别?
- Java 8流和数组操作
- Java Regex捕获组
- Openssl不被视为内部或外部命令