我和一位同事正在进行辩论:
我们有一个单例类,在我们的代码库中的多个位置使用。
最初,该类的设计方式使得您可以获取类对象,但该对象不会被完全初始化。
我的意思是:
mySingleton.getInstance().SomeMethod();
SomeMethod
会导致错误,因为该类未初始化。为了让课程正常工作,必须要做到这一点:
mySingleton.getInstance().initObject();
mySingleton.getInstance().SomeMethod();
无论如何,我的争论是构造函数(使用第一个get实例调用)应该调用initObject
,以便不会抛出任何错误。
您怎么看?
我的同事喜欢它,所以他知道课程何时初始化。 (即他在一个特定的代码行中调用initObject
,并希望首先不需要它。
答案 0 :(得分:8)
您更接近于使用Java实现单例模式的常规方式,而不是您的同事。请看一下Wikipedia。在那里,您将找到3种最常见的Java实现:
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
// Private constructor prevents instantiation from other classes
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
}
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;
}
}
它们都没有使用分离的initObject()
方法(初始化应该在私有构造函数中)。另请注意,如果您使用单独的公共initObject()
方法,则可能存在多线程问题......
编辑在Esko评论之后,我添加了以下实现,在维基百科上不可用。我只想添加 1)单例实例不像上面的3个选项那样懒惰地创建; 2)因为它是一个枚举,所以你不能扩展任何类; 3)这非常非常奇怪。但它似乎在Java社区上大肆炒作,所以就在这里:
public enum Singleton {
INSTANCE;
Singleton() {
/* Your init code goes here */
}
}
答案 1 :(得分:1)
在我看来,使用initObject()
方法是不必要的,似乎打败了拥有单身人士的目的。我觉得任何人使用你的单身人士的假设都会假设他们在调用getInstance()
时会得到一个功能齐全的类实例。为什么要让某些人在他们甚至可以使用它之前调用initObject()
来复杂化并增加代码混淆。为什么有人需要知道对象何时被初始化,是否应该假设检索到的对象已经初始化?
答案 2 :(得分:1)
我会在getInstance()方法中进行初始化检查:
public static MySingleton getInstance() {
if (! isInitialized)
initialize();
return instance;
}
...并将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();
答案 3 :(得分:0)
你的方法更接近单身人士的想法,首先是。
初始化内容对Singleton的用户应该是透明的。一个好方法是甚至不使用构造函数,而是将initObject代码放在静态块中, 在课程定义的开头。
答案 4 :(得分:0)
我不明白为什么你需要一个initObject方法。如果这是一个单例,那么该方法只会被调用一次。我只想在构造函数内部内联方法。
答案 5 :(得分:0)
如果可以避免,请不要使用initObject
方法。
在两种情况下可能是不可避免的:
单例的初始化在应用程序启动期间的某个特定点发生,这是强制这种情况发生的唯一合理方法。
单例的初始化需要只能以这种方式提供的参数;例如从命令行参数构造的参数。
如果你必须有一个init方法,它应该抛出异常,如果它被调用两次。如果getInstance()
方法被过早调用,它也可能是一个好主意。
这些应该适用于@ rsen娜答案中的“传统”范例。