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

时间:2014-02-06 13:22:57

标签: java design-patterns concurrency

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

  1. http://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom
  2. http://en.wikipedia.org/wiki/Singleton_pattern#The_solution_of_Bill_Pugh
  3. 上述主要区别在于,第一个宣称INSTANCE私有,但第二个宣布INSTANCE公开。< / p>

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


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

    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()的内容,这样两个版本都能正常工作。 因此,我想知道使用它们的情况。

3 个答案:

答案 0 :(得分:11)

关于单身人士和按需初始化持有人的习惯用语有几点需要解释。我们走了:

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。

答案 1 :(得分:3)

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

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

答案 2 :(得分:2)

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