参考Effective Java第三版 Joshua J. Bloch

参与编写JDK的大佬,上次看Collections的源码时看见了他的名字,然后翻了翻书,竟然就是他写的!

1.常见的一种:

public class Singleton {
private static final Singleton INSTANCE=new Singleton();
private Singleton(){
//如果没有判断,可以通过反射使用构造函数创建对象,然后就不是单例了
if (INSTANCE!=null){
//throw Exception
}
}
public static Singleton getInstance(){
return INSTANCE;
}
public void doSomething(){
//...
}
}

通过反射:可以看到singleton的两个实例不是同一个。

class Main {
public static void main(String[] args) {
testSingleton();
}    private static void testSingleton() {
Singleton s1 = Singleton.getInstance();
Class<Singleton> clazz = Singleton.class;
try {
Constructor<Singleton> constructor = clazz.getDeclaredConstructor(new Class[]{});
constructor.setAccessible(true);
Singleton s2 = constructor.newInstance(new Class[]{});
System.out.println(s1 == s2);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}

2.用枚举:推荐的方法

优点:引用Effective Java的话:简洁,无偿的提供了序列化机制,绝对防止多次实例化,即使是在面对复杂的序列化或者反射攻击的时候。单元素的枚举类常是实现Singleton的最佳方法。如果Singleton必须扩展一个超类,而不是扩展Enum时,不适宜使用这个方法。

public enum EnumSingleton {
INSTANCE; public void doSomething(){
//...
}
}

按照第一个测试的时候会报错的。

3.序列化

序列化有个问题就是,反序列化时会创建一个新的实例,破坏单例,下面让原来那个类实现Serializable接口。

public class Singleton implements Serializable {

    private static final Singleton INSTANCE=new Singleton();

    private Singleton(){
if (INSTANCE!=null){
try {
throw new Exception("INSTANCE已存在!");
} catch (Exception e) {
e.printStackTrace();
}
}
} public static Singleton getInstance(){
return INSTANCE;
} public void doSomething(){
//...
}
}

测试一下:Effective Java的第9条 使用try-with-resources优于try-finally,关闭资源的时候。

private static void testSerializableSingleton() {
File file=new File("singleton.out");
try(ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream(file));
ObjectInputStream in=new ObjectInputStream(new FileInputStream(file))){ out.writeObject(Singleton.getInstance());
Singleton singleton= (Singleton) in.readObject();
System.out.println(singleton == Singleton.getInstance());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}

打印的结果是false,说明序列化破化了单例,因为反序列化是反射调用了无参构造函数。

解决方法:在类里加入这个方法,详见Effective Java第89条

private Object readResolve() {
return INSTANCE;
}

然后结果就是true了。

不当之处,请指正,谢谢。

用私有构造器或者枚举类型强化Singleton的更多相关文章

  1. 《Effective Java》-——用私有构造器或者枚举类型强化Singleton属性

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  9. 用私有构造器或者枚举类型强化SingleTon(单例)属性

    单例(singleton)就是一个只实例化一次的类.使类成为单例可能会使它的测试变得困难,因为除非它实现了作为其类型的接口,否则不可能用模拟实现来代替这个单例.下面是几种实现单例的方法: 1.共有静态 ...

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

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

随机推荐

  1. Java语言基础二

      1.常量的概述和使用 A:什么是常量 B:Java中常量的分类 常量分类为六种:a.”字符串” b.’字符’ c.整数 d.小数 e.boolern(布尔类型) 返回值为 FALSE和TRUE   ...

  2. html5--6-59 其他常用CSS属性

    html5--6-59 其他常用CSS属性 实例 学习要点 了解opacity属性:透明度设定 了解cursor属性:自定义鼠标样式 了解CSS新单位rem和em的区别 了解轮廓outline的设置 ...

  3. NOSQL安全攻击

    摘自:http://www.infoq.com/cn/articles/nosql-injections-analysis JSON查询以及数据格式 PHP编码数组为原生JSON.嗯,数组示例如下: ...

  4. 最小生成树,并查集的思想 nyoj1239

    #include<stdio.h> #include<string.h> #include<algorithm> using namespace std; int ...

  5. python批量读取txt文件为DataFrame

    我们有时候会批量处理同一个文件夹下的文件,并且希望读取到一个文件里面便于我们计算操作.比方我有下图一系列的txt文件,我该如何把它们写入一个txt文件中并且读取为DataFrame格式呢? 首先我们要 ...

  6. 【NOI 2005】 维修数列

    [题目链接] 点击打开链接 [算法] 本题所运用的也是Splay的区间操作,但是实现较为困难 INSERT操作             将pos splay至根节点,pos+1 splay至根节点的右 ...

  7. 单片机和Linux都想学_换个两全的方法学习单片机

    本节教你如何学习单片机,如何选择合适的开发板和开发工具. 现在我们知道单片机是要学习的,那么怎么去学习单片机?在上一课我们说不要使用老一套的方法学习,实际上是指的两个问题. 第一:选择什么开发板: 第 ...

  8. Bootstrap-CL:下拉菜单

    ylbtech-Bootstrap-CL:下拉菜单 1.返回顶部 1. Bootstrap 下拉菜单(Dropdowns) 本章将重点介绍 Bootstrap 下拉菜单.下拉菜单是可切换的,是以列表格 ...

  9. bzoj1996

    区间dp 其实我们发现对于一段区间我们是这样构造的,每次我们会向两端放数,这样就有四种情况,且必须满足题意,初值是dp[i][i][0]=1,因为第一个人只有一种放法,不分左右.其实看见dp[i][i ...

  10. 【旧文章搬运】Windows内核常见数据结构(基本类型)

    原文发表于百度空间,2008-7-23 ========================================================================== 学内核从基 ...