正确实现按需初始化持有者习惯用法

我有两个版本的“初始化按需持有人习惯用语”:

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

上述主要区别在于,第一个宣布INSTANCE私有 ,但第二个宣布INSTANCE公开

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


对不起,我没有发现在我的应用程序中使用privatepublic之间的区别:

 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)访问修饰符:

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

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

但是, JLS 6.6.1解释说:

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

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

事实上,你放在哪个修改器上并不重要。 如果它是公共的,它仍然无法从Singleton以外的其他类访问。 但是……我认为包私有访问是最好的。 公开它是没有意义的。 私有化迫使编译器做一些技巧。 使其包私有反映了您拥有的内容:从另一个类访问类成员。

2)如何实现单例:

如果您想要考虑序列化,单例实现将会有点困难。 Joshu Bloch在他的“Effective Java”一书中写了一篇关于实现单身人士的伟大部分。 最后,他总结为简单地使用枚举,因为Java枚举规范提供了关于单身人士所需的每一个特征。 当然,这不再使用成语。

3)考虑设计:

在大多数设计决策中,单身人士不再拥有自己的位置。 事实上,如果您必须在程序中放置单例,它可能表明存在设计问题。 请记住:单身人士为某些数据或服务提供全球访问机制。 这不是OOP。

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

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

我使用数字1(私有INSTANCE),因为您通常尝试尽可能使用最窄的范围。 但在这种情况下,由于Holder类是私有的,因此并不重要。 但是,假设有人后来决定将Holder类公开,那么从封装角度来看,数字2可能会有问题(调用者可以绕过getInstance()方法并直接访问静态字段)。