单例模式(Singleton)
保证一个类只有一个实例,并停供一个访问它的全局访问点
Spring中的单例模式完成了后半句话,即提供了全局的访问点BeanFactory,但没有从构造器级别去控制单例,这是因为Spring管理的是是任意的java对象,Spring下默认的Bean均为单例
单例模式为创建型模式,保证了从系统启动到终止,全过程只会产生一个实例
常见的单例模式有饿汉式,懒汉式,注册式,序列化
饿汉式
//饿汉式单例
// 它是在类加载的时候就立即初始化,并且创建单例对象
//优点:没有加任何的锁、执行效率比较高,
//在用户体验上来说,比懒汉式更好
//缺点:类加载的时候就初始化,不管你用还是不用,我都占着空间
//浪费了内存,有可能占着茅坑不拉屎
//绝对线程安全,在线程还没出现以前就是实例化了,不可能存在访问安全问题
public class Hungry {
private Hungry(){}
//先静态、后动态
//先属性、后方法
//先上后下
private static final Hungry hungry = new Hungry();
public static Hungry getInstance(){
// Hungry hungry;
return hungry;
}
}
懒汉式
普通的懒汉式写法是线程不安全
//懒汉式单例
//在外部需要使用的时候才进行实例化
public class LazyOne {
private LazyOne(){}
//静态块,公共内存区域
private static LazyOne lazy = null;
public static LazyOne getInstance(){
//调用方法之前,先判断
//如果没有初始化,将其进行初始化,并且赋值
//将该实例缓存好
if(lazy == null){
//两个线程都会进入这个if里面
lazy = new LazyOne();
}
//如果已经初始化,直接返回之前已经保存好的结果
return lazy;
}
}
安全的懒汉式
//懒汉式单例
//特点:在外部类被调用的时候内部类才会被加载
//内部类一定是要在方法调用之前初始化
//巧妙地避免了线程安全问题
//这种形式兼顾饿汉式的内存浪费,也兼顾synchronized性能问题
//完美地屏蔽了这两个缺点
//史上最牛B的单例模式的实现方式
public class LazyThree {
private boolean initialized = false;
//默认使用LazyThree的时候,会先初始化内部类
//如果没使用的话,内部类是不加载的
private LazyThree(){
synchronized (LazyThree.class){
if(initialized == false){
initialized = !initialized;
}else{
throw new RuntimeException("单例已被侵犯");
}
}
}
//每一个关键字都不是多余的
//static 是为了使单例的空间共享
//保证这个方法不会被重写,重载
public static final LazyThree getInstance(){
//在返回结果以前,一定会先加载内部类
return LazyHolder.LAZY;
}
//默认不加载
private static class LazyHolder{
private static final LazyThree LAZY = new LazyThree();
}
}
注册式单例(Spring中就是用的这种)
//spring中的做法,就是这种注册单例模式
public class BeanFactory{
private BeanFactory(){}
//线程安全的
private static Map<String,Object> ioc = new ConcurrentHashMap<String,Obejct>();
public static Object getBean(String className){
//如果没有存在ioc中,就以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);
}
}
}
序列化式单例
//反序列化时导致单例破坏
public class Seriable implements Serializable {
//序列化就是说把内存中的状态通过转换成字节码的形式
//从而转换一个IO流,写入到其他地方(可以是磁盘、网络IO)
//内存中状态给永久保存下来了
//反序列化
//讲已经持久化的字节码内容,转换为IO流
//通过IO流的读取,进而将读取的内容转换为Java对象
//在转换过程中会重新创建对象new
public final static Seriable INSTANCE = new Seriable();
private Seriable(){}
public static Seriable getInstance(){
return INSTANCE;
}
private Object readResolve(){
return INSTANCE;
}
}