这个单身人员懒惰初始化了吗?

时间:2016-08-29 14:11:07

标签: java singleton lazy-initialization

我有这样的代码:

class Singleton {

    private static final Singleton INSTANCE = new Singleton();

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

    public static Singleton getInstance() {
        return INSTANCE;
    }

}

当我们没有getInstance的任何其他静态方法时,这个单例是否已初始化?据我所知,只有在某些情况下才会初始化类,例如:

  1. 使用new()关键字或using创建类实例 使用class.forName()的反射,可能会抛出 Java中的ClassNotFoundException。
  2. 调用Class的静态方法。
  3. 分配了Class的静态字段。
  4. 使用类的静态字段,它不是常量变量。
  5. 如果Class是顶级类,则执行词法中嵌套在类中的断言语句。
  6. (Surce:http://javarevisited.blogspot.com/2012/07/when-class-loading-initialization-java-example.html#ixzz4IjIe2Rf5

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

5 个答案:

答案 0 :(得分:2)

它立即被初始化,所以不,它不是。通常,延迟初始化意味着您在尝试实际检索它时初始化它,但该字段尚未初始化。

延迟初始化不是关于类初始化 - 它关于包含在其中的字段。在您的情况下,只要加载类,该字段将立即初始化。

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

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类时。在你的情况下,如果它被加载到内存中,那么它的static字段将被初始化(包括你的单例字段)。

答案 1 :(得分:1)

编辑:我错了。只是通过引用类来加载类,如下所示:

System.out.println(Singleton.class); 

只要创建实例,引用静态成员或通过编程方式加载类,类加载器就会加载类:

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

上述语句导致加载类,并且将按照类中的外观顺序处理所有静态成员和块。在您的示例类中,这将导致INSTANCE变量被初始化(以及要打印到System.out的消息)。这证明您的方法不能保证延迟加载。

实现延迟加载单例的更好方法是使用Singleton holder模式:

class Singleton {

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

    private Singleton() {
        // hide constructor
    }

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

}

答案 2 :(得分:1)

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

static int NUMBER = 13;
单词类中的

。而在其他类的主要方法中:

System.out.println(Singleton.NUMBER);

然后,你会看到你的Singleton没有延迟初始化。

但是,当你的领域是静态时最终:

static final int NUMBER = 13;

Singleton是懒惰的初始化。

此外,当您在Singleton类中添加静态和非静态初始化块时:

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

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

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

总之,在某些情况下,您的Singleton可能被视为延迟初始化,但通常不是。

答案 3 :(得分:0)

不,它不是懒惰的初始化。 懒惰意味着第一次打电话或从不。在你的情况下,它不是第一次打电话。

您可以将延迟初始化问题传递给SingletonHolder类。它将在首次getInstance来电时初始化。

public class Singleton {
    private Singleton() {}

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

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

答案 4 :(得分:0)

如果您使用的是.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的线程安全模式。根据您的要求,您可能希望尝试其他模式。