第3项:用私有构造器或者枚举类型强化Singleton属性
Singleton指仅仅被实例化一次的类 [Gamma95]。Singleton通常代表无状态的对象,例如函数(第24项)或者本质上唯一的系统组件。使类称为Singleton会使它的客户端测试变得十分困难,因为除非它实现了作为其类型的接口,否则不可能将模拟实现替换为单例。
实现单例的方法有两种。 两者都基于保持构造函数私有并导出公共静态成员以提供对唯一实例的访问。 在一种方法中,该成员是final字段:
// Singleton with public final field
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() { ... }
public void leaveTheBuilding() { ... }
}
私有构造器只调用一次,用来初始化静态变量Elvis.INSTANCE。由于缺少public或者protect属性的构造器,这就保证了Elvis的全局一致性:一旦Evlis类被实例化,只会存在一个Elvis实例,不多也不少。客户端所做的任何事情都无法改变这一点,但有一点需要注意:享有特权的客户端可以借助AccessibleObject.setAccessible方法反射性地调用私有构造函数(第65项)。 如果你需要防御此攻击,请修改构造函数以使其在要求创建第二个实例时抛出异常。
在实现Singleton的第二种方法中,公有的成员是个静态工厂方法:
// Singleton with static factory
public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis() { ... }
public static Elvis getInstance() { return INSTANCE; }
public void leaveTheBuilding() { ... }
}
对于静态方法Elvis.getInstance的所有调用,都会返回同一个对象引用,所以,永远不会创建其他的Elvis实例(上述提醒依然适用)。
公有域方法的主要好处在于,组成类的成员的声明很清楚地声明了这个类是一个Singleton:公有的静态域是final的,所以该域总是包含同一个对象的引用。第二个好处就是它更加简单。
工厂方法的优势之一在于,它提供了灵活性:在不改变其API的前提下,我们可以改变类是否应该为Singleton的想法。工厂方法返回唯一实例,但是,它可以很容易被修改,比如改成每个调用该方法的线程返回一个唯一的实例。第二个优点是,如果你的应用需要,你可以编写泛型单例工厂(第30项)。使用静态工厂的最后一个优点是方法参考可以用作供应商,例如Elvis::instance是供应商<Elvis>。除非这些优势中的一个是相关的,否则公共领域方法更可取。(A final advantage of using a static factory is that a method reference can be used as a supplier, for example Elvis::instance is a Supplier<Elvis> .Unless one of these advantages is relevant, the public field approach is preferable.)
为了使利用这其中一种方法实现的Singleton类编程可序列化的(第12章),仅仅在声明中加上“implements Serializable”是不够的。为了维护并保证Singleton,必须声明所有实例域都是瞬时(transient)的,并提供一个readResolve方法(第89项)。否则,每次反序列化时,都会创建一个新的实例,在我们的示例中,会导致“假冒的Elvis”。为了防止这种情况,要在Elvis类中加入下面这个readResolve方法:
// readResolve method to preserve singleton property
private Object readResolve() {
// Return the one true Elvis and let the garbage collector
// take care of the Elvis impersonator.
return INSTANCE;
}
实现Singleton还有第三种方法。只需要编写一个包含单个元素的枚举类型:
// Enum singleton - the preferred approach
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() { ... }
}
这种方法类似于公共领域方法,但它更简洁,免费提供序列化机制,并提供了对多个实例化的铁定保证,即使面对复杂的序列化或反射攻击。这种方法可能会有点不自然,但单元素枚举类型通常是实现单例的最佳方法。请注意,如果你的单例必须扩展Enum以外的超类,则不能使用此方法(尽管你可以声明枚举来实现接口)。
第3项:用私有构造器或者枚举类型强化Singleton属性的更多相关文章
- 《Effective Java》-——用私有构造器或者枚举类型强化Singleton属性
Singleton指仅仅被实例化一次的类.Singleton通常被用来代表那些本质上唯一的系统组件,比如窗口管理器或者文件系统.使类成为Singleton会使它的客户端测试变得十分困难,因为无法给Si ...
- 《effective java》读书札记第三条用私有构造器或者枚举类型强化Singleton属性
Singleton指只被实例化一次的类.一般用来搞那些创建很耗资源或者要求系统中只能有一个实例的类. 这个很经常使用.记得曾经实习面试的时候就有这个面试题. 一般採用的方法是将构造器私有化,然后提供一 ...
- Effective Java 之 --- 用私有构造器或者枚举类型强化Singleton属性
Singleton指仅仅被实例化一次的类,通常用来代表那些本质上唯一的系统组件,实现Singleton有三种方法: 1)公有静态成员是个final域,享有特权的用户可以调用AccessibleObje ...
- 【读书笔记 - Effective Java】03. 用私有构造器或者枚举类型强化Singleton属性
实现Singleton(代表本质上唯一的系统组件)的三种方法: 1. 保持私有构造器,导出公有的静态成员,客户端访问该类的唯一实例. 2. 保持私有构造器,公有的成员是静态工厂方法. 3. 单元素的枚 ...
- 用私有构造器或者枚举类型强化Singleton属性
1.Singleton指仅仅被实例化一次的类.Singleton通常被用来代表那些本质上唯一的系统组件,如窗口管理器或者文件系统.使类称为Singleton会使它的客户端调试变的十分困难,因为无法给S ...
- 创建和销毁对象——用私有构造器或者枚举类型强化Singleton属性
参考资料:<Effective Java>.<Java核心技术 卷1>.https://www.cnblogs.com/zhaosq/p/10135362.html 基础回顾 ...
- 第3条:用私有构造器或者枚举类型强化Singleton属性
Singleton是指仅仅被实例化一次的类.通过被用来代表那些本质上唯一的系统组件,比如窗口管理器或者文件系统. 在http://www.cnblogs.com/13jhzeng/p/5256424. ...
- 第三条:用私有构造器或者枚举类型强化Singleton属性
1.使用单元素的枚举类型 public enum Singleton implements Serializable { INSTANCE; private String field; public ...
- 用私有构造器或枚举类型强化Singleton
Singleton指只有一个实例的类,只能被创建一次. 在Java1.5之前实现Singleton有两种方式,都是将构造器设为private并导出公有的静态成员实例. 第一种方式将公有的静态成员实例设 ...
随机推荐
- Java的单向加密算法MD5和SHA——加密和解密
出自:http://www.cnblogs.com/onetwo/p/3875551.html 1.JDK中MD5和SHA加密的主要类 在JDK6.0中,与MD5与SHA密切相关的几个类的类图如下: ...
- PHP数据结构之三 线性表中的单链表的PHP实现
线性表的链式存储:用一组任意的存储单元存储线性表中的数据元素.用这种方法存储的线性表简称线性链表. 链式存储线性表的特点:存储链表中结点的一组任意的存储单元可以是连续的,也可以是不连续的,甚至是零散分 ...
- Python函数之返回值、作用域和局部变量
一.函数返回值 说到返回值,相信大家肯定都认识,没错,就是return. 所谓返回值可以这样理解:函数外部的代码要想获取函数的执行结果,就可以在函数里用return语句把结果返回. 那具体怎么用呢?接 ...
- oracle一个创建用户、创建表空间、授权、建表的完整过程
1.首先我们可以用scott用户以sysdba的身份登录oracle. conn scott/tiger as sysdba 2.然后我就可以来创建用户了. create user zzg ident ...
- 用eclipse+svn插件,上传新项目到svn服务器
给定trunk路径,https://svn.ws.125089.com/public/nlp/3434index/IndexByModelSolr/trunk/. 其中自己的web项目名字是Index ...
- HTML中禁用表中控件的两种方法与区别
在网页的制作过程中,我们会经常使用到表单.但是有时候我们会希望表单上的控件是不可修改的,比如在修改密码的网页中,显示用户名的文本框就应该是不可修改状态的. 在html中有两种禁用的方法,他们分别是: ...
- Docker02 基本命令、开发环境搭建、docker安装nginx、Dockerfile、路径挂载
1 基本命令 1.1 docker相关 centos6.5 安装docker环境 >sudo yum install -y http://mirrors.yun-idc.com/epel/6/i ...
- 10个强大的Javascript表单验证插件推荐
创建一个JavaScript表单验证插件,可以说是一个繁琐的过程,涉及到初期设计.开发与测试等等环节.实际上一个优秀的程序员不仅是技术高手,也应该是善假于外物的.本文介绍了10个不错的JavaScri ...
- Spring MVC 4.2 增加 CORS 支持
转自:http://blog.csdn.net/z69183787/article/details/53102112 Spring MVC 4.2 增加 CORS 支持 跨站 HTTP 请求(Cros ...
- python3安装 feedparser
在看<集体智慧编程>时碰到python3环境下安装feedparser的问题,搜索发现很多人碰到此问题,最终找以下方法解决. how to install feedparser on py ...