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属性的更多相关文章

  1. 《effective java》读书札记第三条用私有构造器或者枚举类型强化Singleton属性

    Singleton指只被实例化一次的类.一般用来搞那些创建很耗资源或者要求系统中只能有一个实例的类. 这个很经常使用.记得曾经实习面试的时候就有这个面试题. 一般採用的方法是将构造器私有化,然后提供一 ...

  2. Effective Java 之 --- 用私有构造器或者枚举类型强化Singleton属性

    Singleton指仅仅被实例化一次的类,通常用来代表那些本质上唯一的系统组件,实现Singleton有三种方法: 1)公有静态成员是个final域,享有特权的用户可以调用AccessibleObje ...

  3. 【读书笔记 - Effective Java】03. 用私有构造器或者枚举类型强化Singleton属性

    实现Singleton(代表本质上唯一的系统组件)的三种方法: 1. 保持私有构造器,导出公有的静态成员,客户端访问该类的唯一实例. 2. 保持私有构造器,公有的成员是静态工厂方法. 3. 单元素的枚 ...

  4. 第3项:用私有构造器或者枚举类型强化Singleton属性

      Singleton指仅仅被实例化一次的类 [Gamma95].Singleton通常代表无状态的对象,例如函数(第24项)或者本质上唯一的系统组件.使类称为Singleton会使它的客户端测试变得 ...

  5. 用私有构造器或者枚举类型强化Singleton属性

    1.Singleton指仅仅被实例化一次的类.Singleton通常被用来代表那些本质上唯一的系统组件,如窗口管理器或者文件系统.使类称为Singleton会使它的客户端调试变的十分困难,因为无法给S ...

  6. 创建和销毁对象——用私有构造器或者枚举类型强化Singleton属性

    参考资料:<Effective Java>.<Java核心技术 卷1>.https://www.cnblogs.com/zhaosq/p/10135362.html 基础回顾 ...

  7. 第3条:用私有构造器或者枚举类型强化Singleton属性

    Singleton是指仅仅被实例化一次的类.通过被用来代表那些本质上唯一的系统组件,比如窗口管理器或者文件系统. 在http://www.cnblogs.com/13jhzeng/p/5256424. ...

  8. 第三条:用私有构造器或者枚举类型强化Singleton属性

    1.使用单元素的枚举类型 public enum Singleton implements Serializable { INSTANCE; private String field; public ...

  9. 用私有构造器或枚举类型强化Singleton

    Singleton指只有一个实例的类,只能被创建一次. 在Java1.5之前实现Singleton有两种方式,都是将构造器设为private并导出公有的静态成员实例. 第一种方式将公有的静态成员实例设 ...

随机推荐

  1. Android 4.0新增Space及GridLayout初谈

    Android 4.0的SDK已经发布,在众多的新增特性中,其中对开发者来说比较重要的特性之一,是新增的两种界面布局方式:Space和Gridlayout,它们跟以往Android版本的sdk有什么不 ...

  2. Extensible Access Control List Framework

    Methods, systems, and products for governing access to objects on a filesystem. In one general embod ...

  3. spring cloud eureka 服务端开启密码认证后,客户端无法接入问题

    Eureka服务端开启密码的认证比较简单 在pom文件中加入: <dependency> <groupId>org.springframework.boot</group ...

  4. 实现网站https

    双击上面的服务器证书 如下图:

  5. 使用Wireshark抓取SNMP Trap包

    Wireshark SNMP Trap 过滤关键字:snmp && udp.dstport == 162

  6. .NET内置的Ajax工作原理

    在期望不执行回发(postback)而从客户端运行服务器代码的情况下,可以使用ClientScriptManager类来调用客户端回调(callback).这称为对服务器执行带外回调.在客户端回调中, ...

  7. ASP.NET Core 中间件 - ASP.NET Core 基础教程 - 简单教程,简单编程

    原文:ASP.NET Core 中间件 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 中间件 上一章节中,我们我们有讲到 Startup 类中的 Confi ...

  8. modern-cpp-features

    C++17/14/11 Overview Many of these descriptions and examples come from various resources (see Acknow ...

  9. 【shell】gerrit同步备份脚本的实现

    本期分享下gerrit如何实现备份,由于之前的一台代码服务器直接down掉,所以为了以防万一,开始备份gerrit数据,有人说,gerrit不过是个审查代码的工具,备份这个做什么,git是分布式代码管 ...

  10. Android 联系人导入导出(VCard格式)

    之前在Android Contact 导入导出 vcf格式(不依赖第三方库)记录了一下依赖Android sdk中的功能导入导出联系人(第一次做java项目内容,有些地方的记录是否正确,暂时我也不知道 ...