当前位置: 首页 > 面试题库 >

正确实施按需初始化持有人习惯用法

贺桐
2023-03-14
问题内容

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

  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公开

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

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

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
解释了:

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

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

实际上,您放置在哪个修饰符上都没有关系。如果它是公共的,那么仍然不能从之外的其他类访问它Singleton。但是…我认为包私有访问是最好的。将其公开是没有道理的。将其设置为私有强制编译器执行一些技巧。将其打包为私有可以反映您所拥有的:从另一个班级访问班级成员。

2)如何实现单例:

如果您要考虑序列化,则单例实现会有些困难。Joshu
Bloch在他的书《有效的Java》中写了一个很重要的部分,关于实现单例。最后,他得出结论,为此简单地使用一个枚举,因为Java枚举规范提供了有关单例所需的所有特征。当然,这不再使用该成语了。

3)考虑设计:

在大多数设计决策中,单例不再拥有其位置。实际上,如果必须在程序中放置一个单例,则可能表明存在设计问题。注意:Singleton为某些数据或服务提供
全局访问 机制。这不是面向html" target="_blank">html" target="_blank">对象的。



 类似资料:
  • 问题内容: 我有一种直觉,即使用Holder惯用法而不将Holder字段声明为final并不是线程安全的(由于Java中不变性的工作方式)。有人可以确认(希望有一些消息来源)吗? 编辑:我绝对希望来源声明,而不仅仅是“它起作用”这样的断言-请解释/证明它是安全的 EDIT2:进行一点点修改以使我的观点更清楚-是否可以确定getAnswer()方法将返回21,而不管调用线程如何? 问题答案: 在类的

  • 我有一个结构如下的项目。 是我的主要应用程序,而和是导入到Project中的两个库。使用了的一些类,使用了的一些类。 在的文件中,我使用了。一切正常。但是,如果我用替换,它就不能从导入类。它给出错误。

  • 问题内容: 我正在设置用于学习JavaEE7中CDI的基本环境。我有以下代码可以启动。只是启动和关闭。 我正在控制台上关注。 有问题的线是。这仅表示依赖注入将不起作用。但是我不确定是什么问题。我已经添加了。我什至没有达到初始化对象的目的,那为什么会出现这个问题呢? Weld的官方文档还给出了阅读此答案后得到的相同代码。“ Antonio Goncalves”撰写的“ Beginning Java

  • 问题内容: 将log4j添加到我的应用程序后,每次执行我的应用程序时,都会得到以下输出: 看来这意味着缺少配置文件。此配置文件应位于何处,什么是良好的入门内容? 我使用纯Java开发桌面应用程序。因此没有网络服务器等… 问题答案: 默认情况下,在上查找名为或的文件。 您可以按照此处所述通过设置系统属性来控制它用来初始化自身的文件(查找“默认初始化过程”部分)。 例如: 将导致在类路径上查找名为的文

  • 问题内容: 用RefluxJS异步初始化数据的正确方法是什么?是否有类似于AngularJS的解决方案,或者Flux的实现与此无关(路由器应该处理此职责)? 问题答案: 在应用程序的顶级组件中,使用方法(docs)触发获取数据的操作。最初渲染组件时将调用此方法。 例如:

  • 我们的项目中有一个不可替代代币状态和不可替代代币合约的自定义实现。我们正在使用下面的代码来发行我们的自定义不可替代代币。 当试图将上面获得的事务构建器转换为有线事务时(< code > builder . towiretransaction(service hub);)我们在下面的堆栈跟踪中得到一个错误。