这个单例是惰性初始化的吗?

Is this singleton lazy initializated?

我有这样的代码:

class Singleton {

    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {
        System.out.println("Singleton constructed.");
    }

    public static Singleton getInstance() {
        return INSTANCE;
    }

}

当我们没有getInstance的其他静态方法时,这个单例是懒初始化的吗?据我所知 class 仅在某些情况下被初始化,例如:

  1. class 的实例是使用 new() 关键字或使用 使用 class.forName() 的反射,可能会抛出 ClassJava 中的 NotFoundException。
  2. 调用了 Class 的静态方法。
  3. 分配了 Class 的静态字段。
  4. 使用了 class 的静态字段,它不是常量变量。
  5. 如果 Class 是顶级 class 并且执行词法嵌套在 class 中的断言语句。

(来源:http://javarevisited.blogspot.com/2012/07/when-class-loading-initialization-java-example.html#ixzz4IjIe2Rf5

因此,当唯一的静态方法是 getInstance 并且构造函数是私有的时,除了使用 getInstance 方法(除了反射)之外,不可能以任何其他方式初始化 Singleton class。所以这个对象只有在我们需要它的时候才被创建,所以它是一个惰性初始化,对吧?或者我错过了什么?

它是立即初始化的,所以不,不是。通常,惰性初始化意味着当您尝试实际检索它但该字段尚未初始化时对其进行初始化。

惰性初始化与 class 初始化无关——它与其中包含的字段有关。在您的情况下,一旦加载 class,该字段将立即初始化。

您的示例适用于延迟初始化:

class Singleton {
    private static Singleton INSTANCE;

    private Singleton() {
        System.out.println("Singleton constructed.");
    }

    public static Singleton getInstance() {
        if(INSTANCE == null) {
            INSTANCE = new Singleton();
        }
        return INSTANCE;
    }
}

这只会在实际请求时构造单例,而不仅仅是当 Singleton class 加载到内存中时。在您的情况下,如果它被加载到内存中,那么它的 static 字段将被初始化(包括您的单例字段)。

编辑:我错了。 A class 不是通过引用它来加载的,如下所示:

System.out.println(Singleton.class); 

A class 由 class 加载程序在创建实例、引用静态成员或以编程方式加载时立即加载:

Class<?> clazz = Class.forName("Singleton"); // fully qualified classname

上面的语句导致加载class,所有静态成员和块将按照在class中出现的顺序进行处理。在您的示例 class 中,这将导致初始化 INSTANCE 变量(并将消息打印到 System.out)。这证明你的方法并不能保证延迟加载。

实现延迟加载单例的更好方法是使用单例持有者模式:

class Singleton {

    private static class Holder {
        private static final Singleton INSTANCE = new Singleton();
    }

    private Singleton() {
        // hide constructor
    }

    public static Singleton getInstance() {
        return Holder.INSTANCE;
    }

}

不,它不是惰性初始化。 懒惰意味着第一次打电话或永远不会。在您的情况下,它不是第一次通话。

您可以将惰性初始化问题传递给 SingletonHolder class。它将在第一次 getInstance 调用时初始化。

public class Singleton {
    private Singleton() {}

    private static class SingletonHolder {
        private static final Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}

您提到了静态方法和私有构造函数。添加另一个静态字段,例如:

static int NUMBER = 13;

在你的单身人士中 class。而在其他class的主要方法中:

System.out.println(Singleton.NUMBER);

然后,你会看到你的单例没有被惰性初始化。

但是,当您的字段是静态的 FINAL 时:

static final int NUMBER = 13;

单例是惰性初始化的。

此外,当您在单例中添加静态和非静态初始化块时 class:

{
    System.out.println("non-static");
}

static {
    System.out.println("static");
}

顺序是:非静态、私有构造函数,然后是静态,因为您正在将对象实例化为静态字段的值。所以,这很棘手:)

总而言之,在某些情况下,您的单例可能被认为是惰性初始化的,但通常情况下并非如此。

如果您使用的是 .NET 4(或更高版本),John Skeet 建议您执行以下操作:

你可以使用System.Lazy类型来让懒惰变得非常简单。您需要做的就是将委托传递给调用 Singleton 构造函数的构造函数 - 使用 lambda 表达式最容易完成。

public sealed class Singleton
{
    private static readonly Lazy<Singleton> lazy = new Lazy<Singleton> (() => new 
                            Singleton());

    public static Singleton Instance { get { return lazy.Value; } }

    private Singleton()
    {
    }
 }

它很简单,性能也很好。如果需要,它还允许您使用 IsValueCreated 属性 检查实例是否已创建。

上面的代码隐含地使用了LazyThreadSafetyMode.ExecutionAndPublication作为Lazy的线程安全模式。根据您的要求,您可能希望尝试其他模式。