【问题标题】:Correct implementation of initialization-on-demand holder idiom按需初始化持有人成语的正确实现
【发布时间】:2014-02-06 13:22:57
【问题描述】:

我有两个版本的“Initialization-on-demand holder idiom”:

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

上面的主要区别是第一个声明INSTANCEprivate,而第二个声明INSTANCEpublic

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


抱歉,我没有发现在我的应用程序中使用 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(),所以两个版本都可以。 因此我想知道使用它们的情况。

【问题讨论】:

    标签: java design-patterns concurrency


    【解决方案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 节) 包含成员或构造函数的声明。

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

    其实,放哪个修饰符都无所谓。如果它是公共的,它仍然无法从Singleton 以外的其他类访问。但是......我认为包私人访问是最好的。公开它是没有意义的。将其设为私有会强制编译器执行一些技巧。将其设为私有包反映了您所拥有的:从另一个类访问一个类成员。

    2) 如何实现单例:

    如果您想考虑序列化,单例实现会有点困难。 Joshu Bloch 在他的“Effective Java”一书中写了一个关于实现单例的精彩部分。最后,他得出结论,只需为此使用枚举,因为 Java 枚举规范提供了关于单例所需的所有特性。当然,那不再使用成语了。

    3) 考虑设计:

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

    【讨论】:

    • 声明一个字段为private会让编译器插入package private方法,对吗?
    • 是和不是。编译器必须强制禁止从类外部访问私有字段。但他也必须考虑 JLS(我引用)。所以诀窍是自动添加包私有方法,现在可以从类外部访问。这些方法被放置在内部类中,但仅在需要时,即如果确实存在像本例中那样的访问权限。此外,对该私有字段的所有直接访问都会与对这些方法的调用进行交换。
    • @Seelenvirtuose,如果 LazyHolder 没有将 INSTANCE 设置为最终版本会出现什么问题
    【解决方案2】:
    private static class LazyHolder {
        $VISIBILITY static final Singleton INSTANCE = new Singleton();
    

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

    【讨论】:

    • 我始终都不使用 - 我使用默认(即省略)可见性,因为在代码中键入和阅读的单词少了一个。
    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 2016-06-03
      • 2014-11-25
      • 2014-10-09
      • 2010-09-15
      • 1970-01-01
      • 2018-09-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多