如果单例实现如下,
class Singleton {
private static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}
此实现与延迟初始化方法有何不同? 在这种情况下,实例将在类加载时创建,并且类本身仅在第一次主动使用时加载(例如,Singleton.getInstance(),而不是在声明实例 Singleton singleton = null; 时加载)
即使使用延迟初始化方法,实例也是在调用 getInstance() 时创建的
我在这里错过了什么吗?
使用延迟初始化,您仅在需要时创建实例,而不是在加载类时创建实例。这样你就可以避免不必要的对象创建。话虽如此,还有其他事情需要考虑。 在延迟初始化中,您提供一个公共 API 来获取实例。在多线程环境中,避免不必要的对象创建带来了挑战。您放置了同步块,这会造成不必要的锁定来检查已创建的对象。所以在这种情况下它就成为一个性能问题。
因此,如果您确定创建对象不会占用任何大量内存,并且它几乎总是会在您的应用程序中使用,那么最好在静态初始化中创建。另外,在这种情况下,请不要忘记将实例设为最终实例,因为它可以确保对象创建正确反映到主内存中,这在多线程环境中非常重要。
请参考IBM关于单例+延迟加载+多线程环境案例的教程
================编辑于2018年9月9日=====================
您还应该查看按需创建对象模式这里。
您也可以调用任何其他静态方法或静态成员变量来加载单例实例。
class Logger {
private static Logger instance = new Logger();
public static String LOG_LINE_SEPERATOR =
System.getProperty("line.separator");
public static Logger getInstance() {
return instance;
}
public static String logPattern() {
return null;
}
}
...
Logger.LOG_LINE_SEPERATOR; // load Logger instance or
Logger.logPattern(); // load Logger instance
出于您提到的原因,这只是一种更复杂的方法,其作用与
相同enum Singleton {
INSTANCE;
}
仅当您担心类可能被初始化但您不想在此时加载单例时,使用延迟初始化才有用。对于大多数情况来说,这已经过头了。
注意:仅引用类并不会初始化类。
例如假设您有一个写得不好的类,在设置某些条件之前无法初始化该类。在这种情况下,
n
必须非零。
public class Main {
public static void main(String ... args) {
Class c= LazyLoaded.class;
System.out.println(c);
}
static class LazyLoaded {
static int n = 0;
static {
System.out.println("Inverse "+1000/n);
}
}
}
打印
class Main$LazyLoaded
首先,单例模式被过度使用。如果您想要“其中一个”,您真正想做的就是在您选择的 DI 框架中将其声明为单例。这实际上是一个配置驱动的渴望单例,并释放了注入模拟以进行正确测试的选项。
为什么不延迟加载?除非你的类在构造中具有大量的初始化例程(我认为这也是一种反模式),否则延迟加载没有任何好处,而且有很多缺点。您只是增加了复杂性,如果做得不正确,则可能会破坏您的程序。正确的方法(如果必须的话)是使用按需初始化持有者习惯用法。
对于延迟加载单例实例,我使用如下。
class Singleton {
private static Singleton instance;
private Singleton(){
}
public static Singleton getInstance() {
if(null==instance){
synchronized(Singleton.class){
if(null==instance){
instance = new Singleton();
}
}
}
return instance;
}
}