java - 按需初始化 holder 习语的正确实现

标签 java design-patterns concurrency

我有两个版本的“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

上面的主要区别在于第一个将INSTANCE声明为private,而第二个将INSTANCE声明为public .

请告诉我应该使用哪个。


抱歉,我没有发现在我的应用程序中使用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) 访问修饰符:

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

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

然而,JLS 6.6.1解释:

Otherwise, if the member or constructor is declared private, then access is permitted if and only if it occurs within the body of the top level class (§7.6) that encloses the declaration of the member or constructor.

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

其实没关系,你放在哪个修饰符上就可以了。如果它是公共(public)的,它仍然不能从 Singleton 以外的其他类访问。但是......我认为包私有(private)访问是最好的。公开它没有意义。将其设为私有(private)会迫使编译器执行一些技巧。将其设为私有(private)反射(reflect)了您所拥有的:从另一个类访问类成员。

2) 如何实现单例:

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

3) 考虑设计:

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

关于java - 按需初始化 holder 习语的正确实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21604243/

相关文章:

关于数据封装的C#设计问题

java - Spring MVC : One step view or Two Step View?

ios - 为什么我的 NSManagedObject 在 [moc save] PerformBlockAndWait 之后没有保留?

java - 在 java 9 Flow 上以一种只有一个订阅者会使用它的方式将数据发布给订阅者

java - ojdbc14.jar 与 ojdbc6.jar

java - XML 模式验证字符串的长度

java - 凋零 vs 生成器 Lombok 库

PHP 工厂方法

iphone - GCD中的 "global queue"和 "main queue"有什么区别?

go - 在 goroutine 中添加处理程序时如何防止数据竞争?