《Effective Java》-——用私有构造器或者枚举类型强化Singleton属性
Singleton指仅仅被实例化一次的类。Singleton通常被用来代表那些本质上唯一的系统组件,比如窗口管理器或者文件系统。使类成为Singleton会使它的客户端测试变得十分困难,因为无法给Singleton替换模拟实现,除非它实现一个充当其类型的接口。
在Java 1.5发行版本之前,实现Singleton有两种方法。
第一种方法,公有静态成员是个final域:
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() { ... }
public void leaveTheBuilding() { ... }
}
第二种方法,公有的成员是个静态工厂方法:
public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis() { ... }
public static Elvis getInstance() { return INSTANCE }
public void leaveTheBuilding() { ... }
}
但要提醒一点:享有特权的客户端可以借助AccessibleObject.setAccessible方法,通过反射机制调用私有构造器。如果需要抵御这种攻击,可以修改构造器,让它在被要求创建第二个实例的时候创建异常。
特殊说明:
AccessibleObject类是 Field、Method 和 Constructor 对象的基类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。
对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用 Field、Method 或 Constructor 对象来设置或获得字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问检查。
也就是说享有特权的可以破坏单例模式:
public class MainClass {
public static void main(String[] args) throws Exception {
// Person person = new Person.Builder("xiaobai","18").sex("男").country("中国").occupation("程序员").build();
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2); // 返回 true
for(Constructor<?> c : s1.getClass().getDeclaredConstructors()) {
c.setAccessible(true); // AccessibleObject
Singleton s3 = (Singleton)c.newInstance();
System.out.println(s3 == s2);
}
}
}
class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return INSTANCE;
}
}
结果:

修改构造器,让它在被要求创建第二个实例的时候创建异常。
class Singleton {
private static int count = 0;
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
if(count == 1)
throw new RuntimeException ("只能初始化一次!");
count++;
}
public static Singleton getInstance() {
return INSTANCE;
}
}
结果:

从Java 1.5发行版本起,实现Singleton还有第三种方法。只需编写一个包含单个元素的枚举类型:
// Enum singleton - the prefered approach
public enum Elvis {
INSTANCE; public void leaveTheBuilding() { ... }
}
这种方法在功能上与公有方法相近,但是它更加简洁,无偿地提供了序列化机制,绝对防止多次实例化,即使在面对复杂的序列化或者反射攻击的时候。虽然这种方法还没有广泛采用,但是单元素的枚举类型已经成为实现Singleton的最佳方法。
注:
文章的内容大多是《Effective Java》书中第二章 第3条:用私有构造器或者枚举类型强化Singleton属性 的内容,文中红色部分是我对文章的补充。
《Effective Java》-——用私有构造器或者枚举类型强化Singleton属性的更多相关文章
- 《effective java》读书札记第三条用私有构造器或者枚举类型强化Singleton属性
Singleton指只被实例化一次的类.一般用来搞那些创建很耗资源或者要求系统中只能有一个实例的类. 这个很经常使用.记得曾经实习面试的时候就有这个面试题. 一般採用的方法是将构造器私有化,然后提供一 ...
- Effective Java 之 --- 用私有构造器或者枚举类型强化Singleton属性
Singleton指仅仅被实例化一次的类,通常用来代表那些本质上唯一的系统组件,实现Singleton有三种方法: 1)公有静态成员是个final域,享有特权的用户可以调用AccessibleObje ...
- 【读书笔记 - Effective Java】03. 用私有构造器或者枚举类型强化Singleton属性
实现Singleton(代表本质上唯一的系统组件)的三种方法: 1. 保持私有构造器,导出公有的静态成员,客户端访问该类的唯一实例. 2. 保持私有构造器,公有的成员是静态工厂方法. 3. 单元素的枚 ...
- 第3项:用私有构造器或者枚举类型强化Singleton属性
Singleton指仅仅被实例化一次的类 [Gamma95].Singleton通常代表无状态的对象,例如函数(第24项)或者本质上唯一的系统组件.使类称为Singleton会使它的客户端测试变得 ...
- 用私有构造器或者枚举类型强化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并导出公有的静态成员实例. 第一种方式将公有的静态成员实例设 ...
随机推荐
- Android 4.0新增Space及GridLayout初谈
Android 4.0的SDK已经发布,在众多的新增特性中,其中对开发者来说比较重要的特性之一,是新增的两种界面布局方式:Space和Gridlayout,它们跟以往Android版本的sdk有什么不 ...
- Extensible Access Control List Framework
Methods, systems, and products for governing access to objects on a filesystem. In one general embod ...
- spring cloud eureka 服务端开启密码认证后,客户端无法接入问题
Eureka服务端开启密码的认证比较简单 在pom文件中加入: <dependency> <groupId>org.springframework.boot</group ...
- 实现网站https
双击上面的服务器证书 如下图:
- 使用Wireshark抓取SNMP Trap包
Wireshark SNMP Trap 过滤关键字:snmp && udp.dstport == 162
- .NET内置的Ajax工作原理
在期望不执行回发(postback)而从客户端运行服务器代码的情况下,可以使用ClientScriptManager类来调用客户端回调(callback).这称为对服务器执行带外回调.在客户端回调中, ...
- ASP.NET Core 中间件 - ASP.NET Core 基础教程 - 简单教程,简单编程
原文:ASP.NET Core 中间件 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 中间件 上一章节中,我们我们有讲到 Startup 类中的 Confi ...
- modern-cpp-features
C++17/14/11 Overview Many of these descriptions and examples come from various resources (see Acknow ...
- 【shell】gerrit同步备份脚本的实现
本期分享下gerrit如何实现备份,由于之前的一台代码服务器直接down掉,所以为了以防万一,开始备份gerrit数据,有人说,gerrit不过是个审查代码的工具,备份这个做什么,git是分布式代码管 ...
- Android 联系人导入导出(VCard格式)
之前在Android Contact 导入导出 vcf格式(不依赖第三方库)记录了一下依赖Android sdk中的功能导入导出联系人(第一次做java项目内容,有些地方的记录是否正确,暂时我也不知道 ...