正确实现按需初始化持有者成语

2022-09-04 02:12:16

我有两个版本的“初始化按需持有者成语”:

  1. http://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom
  2. http://en.wikipedia.org/wiki/Singleton_pattern#The_solution_of_Bill_Pugh

上述之间的主要区别在于,第一个声明为私有,但第二个声明为公共INSTANCEINSTANCE

请告诉我应该使用哪一个。


很抱歉,我没有发现在我的应用程序中使用私有公共之间的区别:

public class Singleton {
    private int x;
    public int getX() {
        return x;
    }

    private Singleton () {}

    private static class LazyHolder {
        //both private and public works
        private static final Singleton INSTANCE = new Singleton();
    }

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

我唯一要做的就是调用类似的东西,所以两个版本都有效。因此,我想知道使用它们的情况。Singleton.getInsance().getX()


答案 1

关于单例和按需初始化持有者成语,有几件事需要解释。来吧:

1) 访问修饰符:

通常,如果字段和方法是私有的,则无法访问它们。如果访问类位于同一包中,则它们必须至少是包私有的(没有修饰符,它是)。因此,实现它的正确方法是:

public class Singleton {
    ...
    private static class LazyHolder {
        static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance() {
        return LazyHolder.INSTANCE;
    }
}

但是,JLS 6.6.1 解释了:

否则,如果成员或构造函数被声明为私有,则当且仅当访问发生在包含成员或构造函数声明的顶级类 (§7.6) 的主体中时,才允许访问。

这意味着,将字段声明为私有仍允许从顶级类内部进行访问。但是编译器必须做一些技巧来绕过私有修饰符:它插入了用于获取和设置此类字段的包私有方法。INSTANCESingleton

实际上,您在其上放置哪个修饰符并不重要。如果它是公共的,则仍然无法从 除 以外的其他类访问它。然而。。。我认为包私有访问是最好的。将其公开是没有意义的。将其设为私有会强制编译器执行一些技巧。将其包设为私有反映了您拥有的内容:访问来自另一个类的类成员。Singleton

2) 如何实现单例:

如果你想考虑序列化,单例实现会变得有点困难。Joshu Bloch在他的书“Effective Java”中写了一个关于实现单例的精彩章节。最后,他总结说,只需使用枚举即可,因为 Java 枚举规范提供了单例所需的所有字符。当然,这不再使用这个成语了。

3)考虑设计:

在大多数设计决策中,单例不再有自己的位置。实际上,如果必须将单例放入程序中,则可能表示存在设计问题。请记住:单例为某些数据或服务提供全局访问机制。这不是OOP。


答案 2
private static class LazyHolder {
    $VISIBILITY static final Singleton INSTANCE = new Singleton();

从消费者的角度来看,$VISIBILITY是公共的还是私有的并不重要,因为LazyHolder类型是私有的。在这两种情况下,该变量只能通过静态方法访问。


推荐