Singleton其实就是单例,即一个类在一个应用程序中只被实例化一次,只有一个对象。
本节讨论三种实现方式
- 私有构造+共有成员
- 私有构造+静态工厂方法+私有成员
- 枚举实现
1、共有成员
构造方法私有,有一个该类类型的公有不可变的实例域。
1.1 code
public class Singleton01 {
public static final Singleton01 singleton01 = new Singleton01();
private Singleton01(){
}
}
当类加载的时候就会创建静态域,同时构造函数私有,因此可以保证唯一实例。
客户端调用:
Singleton01 singleton01 = Singleton01.singleton01;
1.2 分析
-
优点:实现简单;能够明确指导这是一个单例对象,因为公有的静态域时final的,只会存在一个。
-
缺点:可以利用反射调用私有构造方法而二次创建该对象
2、静态工厂方法
将成员定义为私有,提供一个静态方法,返回对象。同时构造方法私有
2.1 code
public class Singleton02 {
/**
* 私有
*/
private static final Singleton02 instance = new Singleton02();
private Singleton02(){
}
/**
* 获取唯一的Singleton02实例
* @return
*/
public static Singleton02 getInstance(){
return instance;
}
}
对于静态方法getInstance的调用,返回的都是同一个Singleton02实例。
客户端调用:
Singleton02 instance = Singleton02.instance;
2.2 分析
-
优点:(1)灵活,只需要改变
getInstance
方法就能修改是否时单例(例如改为每个线程创建一个);(2)可以使用方法引用提供参数,写起来优雅;(3)可以编写范型Singleton工厂,30条在分析。 -
缺点:和1相比,不明显是单例;没有解决反射破坏单例的问题。
注意:
上述两种方法,出了反射调用私有构造方法破坏单例外,反序列化也会破坏单例,针对反序列化的问题,可以声明实例域是瞬时的,并提供一个 readResolves
,这点在第89条讨论。
private static final transient Singleton02 instance = new Singleton02();
3、使用枚举
将想要单例的类定义为一个枚举类型
3.1 code
public enum Singleton03 {
INSTANCE("zhangsan","male");
private String name;
private String gender;
Singleton03(String name, String gender) {
this.name = name;
this.gender = gender;
}
}
客户端使用:
Singleton03 instance = Singleton03.INSTANCE;
3.2 分析
-
优点:安全,枚举的内部实现就禁用了反射和反序列机制。
-
缺点:如果单例类必须扩展一个超类,这时候就不适用了。因为枚举不能继承其他类。