【问题标题】:Bill Pugh Singleton - why the container? [duplicate]Bill Pugh Singleton - 为什么是容器? [复制]
【发布时间】:2021-11-04 18:04:39
【问题描述】:

我有以下代码:

package DesignPatterns;

public class SingletonProblem
{
    public static void main(String[] args) {
        System.out.println(BillPughSingleton.getInstance());
        System.out.println(BillPughSingletonWithoutContainer.getInstance());
    }
}

class BillPughSingletonWithoutContainer
{
    private static BillPughSingletonWithoutContainer instance = new BillPughSingletonWithoutContainer();

    static {
        System.out.println("Static is now loaded in BillPughSingletonWithoutContainer");
    }

    private BillPughSingletonWithoutContainer() {}

    public static BillPughSingletonWithoutContainer getInstance()
    {
        return instance;
    }
}

class BillPughSingleton
{
    private static class Container
    {
        public static BillPughSingleton instance = new BillPughSingleton();
    }

    private BillPughSingleton() {}

    public static BillPughSingleton getInstance()
    {
        return Container.instance;
    }
}

输出是:

DesignPatterns.BillPughSingleton@36baf30c
Static is now loaded in BillPughSingletonWithoutContainer
DesignPatterns.BillPughSingletonWithoutContainer@5ca881b5

如果在没有容器的示例中,当调用 BillPughSingletonWithoutContainer.getInstance() 时实例似乎也被延迟加载,为什么容器有用?

在热切加载(BillPughSingletonWithoutContainer 声称是什么)时,我预计输出是:

Static is now loaded in BillPughSingletonWithoutContainer
DesignPatterns.BillPughSingleton@36baf30c
DesignPatterns.BillPughSingletonWithoutContainer@5ca881b5

代替:

DesignPatterns.BillPughSingleton@36baf30c
Static is now loaded in BillPughSingletonWithoutContainer
DesignPatterns.BillPughSingletonWithoutContainer@5ca881b5

那么换句话说,使用容器有什么好处(在 Java 中)?

【问题讨论】:

  • 线程安全,一般来说。
  • 所以 BillPughSingletonWithoutContainer 不是线程安全的?你能在答案中解释一下吗,因为我不明白这个:)。
  • 它是完全线程安全的,只是不 lazy。 Pugh 模式是一种延迟加载静态实例的安全/高效方式
  • 好的,你能在答案中展示 BillPughSingletonWithoutContainer 是如何不延迟加载的吗?
  • 您的第一个例子不是 Bill Pugh Singleton。根据定义,Bill Pugh Singleton 使用嵌套类。

标签: java


【解决方案1】:

BillPughSingletonWithoutContainer 中,static {} 初始化程序块总是在第一次引用类时执行(这基本上来自代码的imports)。这里没有延迟初始化技巧。根据docs,正确使用静态初始化器是......初始化静态字段。

这与 静态内部类 不同,后者仅在 类第一次被引用时加载 - 仅在您的 BillPughSingleton 的构造函数中发生(当它调用而不是在加载时)。 https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom

【讨论】:

  • 您能否给出一个带有 cmets 的代码示例,以显示实例是在 import 语句上创建的?我无法重现这一点。我已将 BillPughSingletonWithoutContainer 移动到子包并导入它,但输出是相同的。
  • @Douam 它不是在导入时创建的,因为导入只是源代码的工件。但是,当加载任何 references BillPughSingletonWithoutContainer 的代码时,该类将被加载并初始化。
  • @Douma 我需要更好地限定它:当其他类被加载并在字段、方法签名或方法返回类型中引用该类时,或者当引用该类的代码被执行时,它将被加载以某种方式(局部变量、非常量静态字段、静态方法、构造函数调用等)的类
【解决方案2】:

这组类展示了定义单例的三种常用方法 - 两种方法与您定义的方法类似,一种方法使用 enum。每个帮助中的 System.out.println() 发现了初始化的差异:

enum SingletonEnum {
    INSTANCE;

    public static void hello() { System.out.println("hello() SingletonEnum"); }

    public static SingletonEnum getInstance() {
        System.out.println("SingletonEnum getInstance "+INSTANCE);
        return INSTANCE;
    }

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

    {
        System.out.println("SingletonEnum instance "+this);
    }
}
class SingletonClass {
    public static final SingletonClass INSTANCE = new SingletonClass();

    public static void hello() { System.out.println("hello() SingletonClass"); }

    public static SingletonClass getInstance() {
        System.out.println("SingletonClass getInstance "+INSTANCE);
        return INSTANCE;
    }

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

    {
        System.out.println("SingletonClass instance "+this);
    }
}
class SingletonContainer
{
    private static class Container {
        public static SingletonContainer instance = new SingletonContainer();
    }
    public static void hello() { System.out.println("hello() SingletonContainer"); }

    public static SingletonContainer getInstance()
    {
        System.out.println("SingletonContainer getInstance "+Container.instance);
        return Container.instance;
    }

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

    {
        System.out.println("SingletonContainer instance "+this);
    }
}

BillPughSingleton 的主要好处是实例化仅在调用getInstance() 时发生,而对于其他方法,第一个方法访问hello() 会导致实例初始化。 enum 版本是一个更简单的声明,以确保只有一个实例(其他需要私有构造函数,我的示例中省略了)。

public static void main(String[] args) {
    System.out.println("1");
    SingletonClass.hello();
    System.out.println("2");
    SingletonContainer.hello();
    System.out.println("3");
    SingletonEnum.hello();

    System.out.println("4");
    SingletonClass.getInstance();
    System.out.println("5");
    SingletonEnum.getInstance();
    System.out.println("6");
    SingletonContainer.getInstance();
    System.out.println("7");
}

这会打印类似这样的内容,因此容器版本的构建被推迟到它的getInstance 调用:

1
SingletonClass instance SingletonClass@5acf9800
SingletonClass static
hello() SingletonClass
2
SingletonContainer static
hello() SingletonContainer
3
SingletonEnum instance INSTANCE
SingletonEnum static
hello() SingletonEnum
4
SingletonClass getInstance SingletonClass@5acf9800
5
SingletonEnum getInstance INSTANCE
6
SingletonContainer instance SingletonContainer@5ca881b5
SingletonContainer getInstance SingletonContainer@5ca881b5
7

【讨论】:

    猜你喜欢
    • 2018-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-27
    • 2015-12-06
    • 1970-01-01
    • 2014-07-16
    • 1970-01-01
    相关资源
    最近更新 更多