首先,我们先来复习一下什么是单例模式:
单例模式是一种创建型设计模式, 让你能够保证一个类只有一个实例, 并提供一个访问该实例的全局节点。
那么问题来了? 什么样的类只能创建一个实例呢?
比如,在一个系统中,总有一些东西是全局共享且很少改变的。 比如数据库的url , password, username配置信息。我们可以把它放在一个单例类中,然后系统中所有的地方需要用到这些配置的时候,就可以直接通过全局访问点获得此单例类的实例,然后通过调用相关的方法获取相关的配置。
单例模式的UML非常简单,首先是私有化构造函数,然后是增加一个对外的对象全局访问点。
普通实现,采用方法同步锁,线程安全但是使用时性能比较差
public class Singleton {
//采用static修饰保证此类只有一个实例
private static Singleton instance;
private Singleton (){}
//用synchronized保证此类不会实例化此类两次
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
采用双重检查锁机制实现安全的懒汉式单例类,线程安全,性能相比前者较高
public class Singleton {
//volatile保证此类变量的可见性,保持线程安全
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
普通实现
public class Singleton {
//final保证了此变量的不可变性,防止反射攻击
private static final Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
静态代码块的方式实现单例类
/**
* 静态代码块方式实现单例类,此种方式和和饿汉式效果一样,都是在类加载的时候进行实例化
*/
public class StaticBlockSingleton {
//final保证变量的不可变性,防止反射攻击
private static final StaticBlockSingleton instance;
private StaticBlockSingleton(){}
//静态代码块也是在类加载是进行初始化的,且只执行一次
static{
try{
instance = new StaticBlockSingleton();
}catch(Exception e){
throw new RuntimeException("Exception occured in creating singleton instance");
}
}
public static StaticBlockSingleton getInstance(){
return instance;
}
}
此种方式相比懒汉式的安全实现比较简单,主要是final和静态内部类的机制发挥作用
public class Singleton {
//静态内部类只有在第一次使用时加载
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
枚举实现是利用java内部机制的单例最佳实现,推荐使用。
/**
* 枚举单例实现
* 其实我们采用普通私有化构造器方法是实现单例类的方式在反射面前并不可靠
* 因为一些客户端使用反射机制能够实例化单例类的第二个实例甚至第n个实例,
* 如果我们想要抵御这样的攻击,我们可以人为的增加限制,让构造器进行自我检查,就是在构造器尝试实例化第二个实例的时候抛出异常
* 单例枚举单例可以不用那么麻烦就能做到抵御前面的攻击,枚举对象天然就是单例
*/
public enum EnumSingleton {
INSTANCE;
private String name;
private String code;
public String getName(){
return name;
}
public String getCode(){
return code;
}
public void setName(String name){
this.name = name;
}
public void setCode(String code){
this.code = code;
}
}
import java.io.Serializable;
/**
* 可序列化的单例类,用于分布式系统的单例类
*/
public class SerializedSingleton implements Serializable {
private static final long serialVersionUID = -78834253243544235L;
private SerializedSingleton(){}
private static class SingletonHelper{
private static final SerializedSingleton instance = new SerializedSingleton();
}
public static SerializedSingleton getInstance(){
return SingletonHelper.instance;
}
/**
*通过添加这个方法能防止序列化所造成得到的实例不一致的现象
*/
protected Object readResolve() {
return getInstance();
}
}