Java 单例类设计-空对象
我和一位同事正在进行辩论: 我们有一个单例类,它在代码库中的多个位置使用 最初,类的设计方式是,您可以获取类对象,但该对象不会完全初始化 我的意思是:Java 单例类设计-空对象,java,multithreading,singleton,class-design,Java,Multithreading,Singleton,Class Design,我和一位同事正在进行辩论: 我们有一个单例类,它在代码库中的多个位置使用 最初,类的设计方式是,您可以获取类对象,但该对象不会完全初始化 我的意思是: mySingleton.getInstance().SomeMethod(); SomeMethod将导致错误,因为类未初始化。要使类正常工作,必须执行以下操作: mySingleton.getInstance().initObject(); mySingleton.getInstance().SomeMethod(); 无论如何,我的争论
mySingleton.getInstance().SomeMethod();
SomeMethod
将导致错误,因为类未初始化。要使类正常工作,必须执行以下操作:
mySingleton.getInstance().initObject();
mySingleton.getInstance().SomeMethod();
无论如何,我的争论是构造函数(用第一个get实例调用)应该调用initObject
,这样就不会抛出错误
你觉得怎么样
我的同事喜欢另一种方式,因此他知道类何时初始化。(也就是说,他在一行特定的代码中调用
initObject
,并希望没有其他东西需要它)。在我看来,拥有initObject()
方法是不必要的,而且似乎违背了拥有单例的目的。我觉得任何使用您的单例的人都会假设,当他们调用getInstance()
时,他们会得到该类的完整功能实例。为什么要求某人在使用它之前调用initObject()。为什么有人需要知道对象何时被初始化,难道不应该假设检索到的对象已经被初始化了吗?您比您的同事更接近Java中实现singleton模式的通常方式。请看一看。在这里,您可以找到3种最常见的Java实现:
传统简易方法
“比尔·普格的解决方案”
使用同步的传统简单方法
它们都没有使用单独的initObject()
方法(初始化应该在私有构造函数中)。另外请注意,如果您有一个单独的、公共的initObject()
方法,则可能会出现多线程问题
顺便说一句,就我个人而言,我宁愿使用“比尔·普格”替代方案,但这三种方法都是有效的
编辑在类Esko评论之后,我添加了以下实现,这在Wikipedia上是不可用的。我只想补充一点:1)singleton实例不是像上面的3个选项那样懒洋洋地创建的;2) 因为它是枚举,所以不能扩展任何类;这是非常非常奇怪的。但它似乎在Java社区中大肆宣传,所以它就在这里:
枚举方式
我将在getInstance()方法中进行初始化检查:
。。。并将initialize()方法设为私有
编辑:显然我误解了这个问题。主要问题是initObject()应该在构造函数内部还是外部。正确答案可能取决于实际问题,但我认为它通常应该在构造函数内部,因为如果将initObject()方法置于构造函数外部的唯一目的是告诉每个人初始化发生的位置,那么您最好写一条注释:
// Attention everyone!! Object is initialized here!!1!11!
MySingleton instance = MySingleton.getInstance();
// Object is NOT initialized here!!11!1!!
MySingleton instance2 = MySingleton.getInstance();
首先,你的方法更接近单身的想法
初始化内容对Singleton的用户应该是透明的。这样做的一个好方法甚至不是使用构造函数,而是将initObject代码放在一个静态块中,
在类定义的开头。我不明白为什么需要initObject方法。若这是一个单例,那个么该方法无论如何只会得到一次调用。我只是将方法内联到构造函数本身中。如果可以避免,请不要使用initObject
方法
在两种情况下可能不可避免:
- 单例的初始化必须在应用程序启动过程中的某个特定点进行,这是强制进行初始化的唯一合理方法
- singleton的初始化需要只能以这种方式提供的参数;e、 g.由命令行参数构造的参数
如果您必须有一个init方法,那么如果它被调用两次,它应该抛出一个异常。如果异常调用得太早,那么getInstance()
方法也可以抛出异常
这些应该应用于@rsenna答案中的“传统”示例。如果您想遵循这种方式,必须将初始化检查放入同步的块中(以获得线程安全)。+1表示“如果唯一目的是放置initObject()构造函数之外的方法是告诉每个人初始化发生的位置,你也可以写一条注释”。在许多情况下,最好的解决方案是首先不使用单例,而是使用普通的实例对象,并简单地将实例注入到需要它的类中(手动将其传递到构造函数或类似的构造函数中,或使用IoC框架)。奇怪的是,所有这些都没有使用enum,这是在Java中实现单例的最简单方法。@Esko:事实上,我发现enum实现在语义上有点奇怪(基本上是Java WTF),但好的,我将编辑我的答案并添加它…谢谢团队,我同意你们所有人的意见。我认为initObject是一些令人沮丧的调试的结果..或者其他…无论如何,得到反馈很好。谢谢团队,我同意你们所有人的意见。我认为initObject是一些令人沮丧的调试的结果..或者其他…无论如何,得到反馈很好反馈,再次感谢。
public class Singleton {
// Private constructor prevents instantiation from other classes
private Singleton() {}
/**
* SingletonHolder is loaded on the first execution of Singleton.getInstance()
* or the first access to SingletonHolder.INSTANCE, not before.
*/
private static class SingletonHolder {
public static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
public class Singleton {
// volatile is needed so that multiple threads can reconcile the instance
// semantics for volatile changed in Java 5.
private volatile static Singleton singleton;
private Singleton() {}
// synchronized keyword has been removed from here
public static Singleton getSingleton() {
// needed because once there is singleton available no need to acquire
// monitor again & again as it is costly
if (singleton == null) {
synchronized (Singleton.class) {
// this is needed if two threads are waiting at the monitor at the
// time when singleton was getting instantiated
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
public enum Singleton {
INSTANCE;
Singleton() {
/* Your init code goes here */
}
}
public static MySingleton getInstance() {
if (! isInitialized)
initialize();
return instance;
}
// Attention everyone!! Object is initialized here!!1!11!
MySingleton instance = MySingleton.getInstance();
// Object is NOT initialized here!!11!1!!
MySingleton instance2 = MySingleton.getInstance();