参考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. iOS开发UIScrollView常见属性和方法

    一.ScrollView常用方法和属性 @property(nonatomic)CGPoint contentOffset; 设置滚动的偏移量 @property(nonatomic)CGSize c ...

  2. mysql无法远程访问

    最近使用Navicat for MySQl访问远程mysql数据库,出现报错, 显示“1130 - Host'xxx.xxx.xxx.xxx' is not allowed to connect to ...

  3. 正则表达式、Calendar类、SimpleDateFormat类、Date类、BigDecimal类、BigInteger类、System类、Random类、Math类(Java基础知识十四)

    1.正则表达式的概述和简单使用 * A:正则表达式(一个字符串,是规则)     * 是指一个用来描述或者匹配一系列符合某个语法规则的字符串的单个字符串.其实就是一种规则.有自己特殊的应用. * B: ...

  4. for、while循环(java基础知识四)

    1.循环结构概述和for语句的格式及其使用 * 什么是循环结构 循环语句可以在满足循环条件的情况下,反复执行某一段代码,这段被重复执行的代码被称为循环体语句,当反复执行这个循环体时,需要在合适的时候把 ...

  5. codeforces 459 B.Pashmak and Flowers 解题报告

    题目链接:http://codeforces.com/problemset/problem/459/B 题目意思:有 n 朵 flowers,每朵flower有相应的 beauty,求出最大的beau ...

  6. data对象转化成后端需要的json格式

    data=JSON.stringify(json_data); $.ajax({type:'post',url:url+'warehouse/create_alliance_out/',data:da ...

  7. Bootstrap-CSS:响应式实用工具

    ylbtech-Bootstrap-CSS:响应式实用工具 1.返回顶部 1.  Bootstrap 响应式实用工具 Bootstrap 提供了一些辅助类,以便更快地实现对移动设备友好的开发.这些可以 ...

  8. 通过配置Mysql参数提高写入速度(整理)

    1) innodb_buffer_pool_size 如果用Innodb,那么这是一个重要变量.相对于MyISAM来说,Innodb对于buffer size更敏感.MySIAM可能对于大数据量使用默 ...

  9. 指针 * &

    int main() { ; //定义int变量updates int * p_updates; //定义指针p_updates p_updates=&updates;//将updates的地 ...

  10. View Programming Guide for iOS ---- iOS 视图编程指南(五)---Animations

      Animations Animations provide fluid visual transitions between different states of your user inter ...