【问题标题】:Why private static field = new Singleton is not lazy in Java?为什么 private static field = new Singleton 在 Java 中不懒惰?
【发布时间】:2014-06-30 09:36:49
【问题描述】:

我阅读了很多关于 Singleton 的文章,其中大多数作者都说 Java 中 Singleton 的这种变体:

public class Singleton {
    private static Singleton instance = new Singleton();

    private Singleton() {}
    public static Singleton getInstance() {
        return instance;
    }
}

不懒那么渴望)。

但我不明白为什么,Singleton() 构造函数只会在 Singleton 类初始化时被调用。我知道有几个原因可以触发类初始化:

  1. new 与构造函数一起使用(但在这种情况下构造函数是私有的)。
  2. 访问或设置静态字段(此处为私有)。
  3. 使用静态方法。
  4. 有反射:Class.forName("Singleton")

所以这里我们的对象将只在使用静态方法getInstance()(我猜它仍然是LAZY初始化)和反射(但反射会破坏很多单例变化,除了@ 987654327@也许)。

也许我看不到明显的东西,请解释一下,我哪里错了?

【问题讨论】:

  • 你的代码懒惰创造的一个例子。只要没有其他方式使用你的类,正如你自己猜到的那样,它是按需延迟创建的(使用反射只是另一种要求初始化的方式)。所以你是对的。
  • 这是我统治世界的计划:1) 更喜欢enum Singleton 模式。 2) 意识到单例一开始是个坏主意,不管你如何实现它们。 3) 让人们了解 stackoverflow 为什么单例是一个坏主意。 4) 退休,在海滩上喝玛格丽塔酒。

标签: java singleton


【解决方案1】:

基本上这是懒惰程度的问题。它很懒惰,因为它在类初始化之前不会构造单例,但它很急切的是,可能在某些情况下,您希望在不初始化单例本身的情况下使用该类。

例如:

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

    private Singleton() {}

    public static Singleton getInstance() {
        return instance;
    }

    public static void sayHello() {
        System.out.println("Hello!");
    }
}

调用Singleton.sayHello() 将实例化单例,即使我们不希望它...所以它不会像它可能的那样懒惰。

你可以使用嵌套类型来解决这个问题:

public final class Singleton {
    private Singleton() {}

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

    public static void sayHello() {
        System.out.println("Hello!");
    }

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

现在Singleton.Holder 只能使用getInstance 方法进行初始化。它是惰性且线程安全的,没有锁定。

根据我的经验,通常单例类的 only 静态方法是 getInstance 方法,在这种情况下它们是等价的(假设您不使用反射来以某种方式初始化类型,因为示例)。

【讨论】:

  • 是的,我同意你的看法。但是我们还需要 Singleton 中的其他静态方法吗?总结一下:没有其他静态方法是懒惰的,但有其他静态方法是急切的,对吧?
  • 是的,我认为这是最令人满意的答案。虽然它是一个非常困难的问题,但真正抛出这个问题的是它是一个单例,因此有一个对自身的引用。这已经有点模糊了“懒惰”这个词到底指的是什么......延迟实例化或延迟初始化?在单例的上下文中,它通常在架构上是相同的:/
  • @marshall:嗯,对于其他静态方法,它“在调用其他方法时非常渴望”。例如,在“应用程序启动时”方面与渴望不同。在大多数情况下,我个人会尽量避免使用单例:)
  • 我也避免了:它看起来不太好,如果你必须为单元测试添加一个静态方法 resetSingleton(),它使用反射来重置实例字段
【解决方案2】:

它不是惰性的,因为一旦加载了类,就会创建 singeton 对象。
懒惰的 Singleton 会在第一次使用时创建对象。

【讨论】:

  • 但是正如我在问题中所说的那样,类只会在 getInstance() 方法上加载。您没有阅读或不同意我的观点?请告诉我为什么,我不明白。
  • 类可以被加载,你可以引用它。例如 Singleton theSingleton;
  • 我不同意你的观点。 Singleton theSingleton 不会加载类。
  • @marshall:它可能会加载类但不会初始化它。这可能是特定于虚拟机的......不确定。
  • 至少在以后检查单例的时候:if (theSingleton instanceof IFastSorter) { ...} else ..,它被加载了。
【解决方案3】:

在我看来,Lazy 的目标是控制 Singleton 实例的创建时间。 在这个例子中,没有控制,因为有一个类变量(静态成员在类初始化时初始化,而不是在调用 getInstance() 方法时)。

这样,就无法控制 Singleton 的创建时间。

但是,如果您在 getInstance 中初始化变量,您就可以控制何时创建 Singleton。

正如你所说,单例的例子很多。如果需要,我总是尝试使用 Enums。 你可以在这里找到例子:https://connected2know.com/programming/java-singleton-pattern/

【讨论】:

    猜你喜欢
    • 2015-09-06
    • 1970-01-01
    • 1970-01-01
    • 2021-07-23
    • 2021-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-21
    相关资源
    最近更新 更多