1. 单例模式介绍
单例模式属于创建型模式。
1.1 应用场景
保证系统启动到终止一个类仅有一个实例,实现资源共享,并提供一个访问实例的全局访问点。
Spring中的Bean默认就是单例的,而BeanFactory则是全局访问点。
1.2 单例模式实现案例
- 日历
- IOC容器
- 配置文件
1.3 常用写法
- 饿汉式
- 懒汉式
- 注册登记式
- 枚举式
2. 单例模式实现
2.1 饿汉式
线程安全
优点: 效率高
缺点: 不适用也会占用空间
public class Single {
private Single(){}
private static final Single single = new Single();
public static Single getInstance(){
return single;
}
}
2.2 饿汉式
线程不安全
public class LazySingle {
private LazySingle(){}
private static LazySingle lazy = null;
public static LazySingle getInstance(){
if(lazy == null){
lazy = new LazySingle();
}
return lazy;
}
}
要想线程安全在getInstance方法中加上synchronized即可,不过这样子做性能很差。可以使用内部类方式。
线程安全
//默认使用LazySingle的时候,会先初始化内部类,如果没使用的话,内部类不加载
public class LazySingle {
private boolean flag = false;
private LazySingle(){
//防止反射侵入创建实例
synchronized (LazySingle.class){
if(flag == false){
flag = !flag;
}else{
throw new RuntimeException("单例已被侵犯");
}
}
}
public static LazySingle getInstance(){
return LazyInstance.LAZY;
}
//默认不加载
private static class LazyInstance{
private static final LazySingle LAZY = new LazySingle();
}
}
2.3 注册登记式
线程不安全
//Spring中的就是用这种注册式单例
public class BeanFactory {
private BeanFactory(){}
private static Map<String,Object> ioc = new ConcurrentHashMap<String,Object>();
public static Object getBean(String className){
if(!ioc.containsKey(className)){
Object obj = null;
try {
obj = Class.forName(className).newInstance();
ioc.put(className,obj);
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}else{
return ioc.get(className);
}
}
}
2.4 枚举式
枚举本身就是初始化好的实例,所以是绝对安全的单例。
public enum Single {
INSTANCE(){
private int age;
};
}
3. 注意
需要注意的是单例模式在序列化和反序列化过程中,单例会遭到破坏。
因为序列化就是把内存中的状态通过转换成字节码的形式,从而转换一个IO流,写入到特定地方(磁盘、网络IO)。
反序列化就是将已经持久化的字节码内容,转换为IO流通过IO流的读取,将读取的内容转换为Java对象,在转换过程中会重新创建对象。
解决方案:重写readResolve()即可。
设计模式是程序员内功之一,切记不要死记硬背,用心理解。下篇将介绍原型模式。