这个单例是否延迟初始化



我有这样的代码:

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. 类的实例是使用new()关键字或使用使用class.forName()进行反射,这可能会抛出Java中的ClassNotFoundException .
  2. 调用Class的静态方法。
  3. 指定Class的静态字段。
  4. 使用class的静态字段,而不是常量变量。
  5. 如果Class是一个顶级类,并且在类中执行词法嵌套的assert语句。

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

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

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

static int NUMBER = 13;

在Singleton类中。在其他类的main方法中:

System.out.println(Singleton.NUMBER);

那么,你会看到你的Singleton没有延迟初始化。

但是,当你的字段是静态的FINAL:

static final int NUMBER = 13;

Singleton是延迟初始化的

而且,当你在Singleton类中添加静态和非静态初始化块时:

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

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

总而言之,在某些情况下,你的Singleton可能被认为是延迟初始化的,但通常不是这样。

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

延迟初始化不是关于类的初始化,而是关于类中包含的字段。在您的例子中,一旦类被加载,字段将立即初始化。

你的例子适应使用延迟初始化:

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字段将被初始化(包括您的单例字段)。

编辑:我错了。类不是通过引用来加载的,像下面这样:

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;
    }
}

不,它不是惰性初始化。懒惰的意思是第一次打电话就偷懒,否则就不偷懒。在你的情况下,它不是第一次呼叫。

可以将延迟初始化问题传递给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;
    }
}

如果您正在使用。net 4(或更高版本),John Skeet建议您这样做:

你可以使用系统。懒惰型让懒惰变得真正简单。你所需要做的就是传递一个委托给调用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的线程安全模式。根据您的需求,您可能希望尝试其他模式。

最新更新