设计模式二 单例模式
0、基本定义
单例模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点.
通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法,让类自身负责保存它的唯一实例。
这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。
要求:
1、构造私有方法
2、定义一个实例,自己实例化
3、获取实例
须 解决一个并发访问的线程安全问题。
spring 中默认bean加载都是单例。
1、实现方式
1.1、饿汉式
不管用不用,先new一个对象
public class Emperor { private static final Emperor emperor = new Emperor(); private Emperor() { } public static Emperor getInstance() { return emperor; } public void say() { System.out.println("I am Empror..A. " + this); } }
1.2、懒汉式
默认加载的时候不实例化,在需要用的时候,才加载
public class Singleton {
private static Singleton singleton = null;
private Singleton() {}
/**
* 加 Synchronized, 解决 thread safe
* 性能 差!!!
* @return
*/
public static synchronized LazyTwo getLazy() {
if (lazy == null) {
lazy = new LazyTwo();
}
return lazy;
}
}
1.3 内部类
/** * 内部类 * @author zzf * @date 2018/8/25 00:14. */ public class LazyThree { private static boolean initial = false; //默认使用LazyThree 的时候,会先初始化内部类 //如果没有使用的话,内部类是不会加载的 private LazyThree() { // synchronized (LazyThree.class) { if (initial) { initial = !initial; } else { throw new RuntimeException("单利侵入初始化"); } } } private static final LazyThree getInstance() { return LazyHolder.LAZY_THREE; } private static class LazyHolder { private static final LazyThree LAZY_THREE = new LazyThree(); } }
1.4 注册登记式
每使用一次,都注册到容器中,下次去对象时,直接从缓存中取值,以保证每次获取的都是 同一个对象。
Spring IOC
/** * 注册式 * @author zzf * @date 2018/8/25 00:25. */ public class RegisterMap { private static Map<String, Object> register = new HashMap(); public static RegisterMap getInstance(String name) { if (name == null) { name = RegisterMap.class.getName(); } if (register.get(name) == null) { try { register.put(name, RegisterMap.class.newInstance()); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } return (RegisterMap) register.get(name); } }
1.5 枚举
注册登记式的一种
public enum RegisterEnum { INSTANCE; public static RegisterEnum getInstance() { return INSTANCE; } }
1.6 序列化和反序列化
序列化与反序列化的时候出现多例: 重写 readResolve()
public class Seriable implements Serializable{ public final static Seriable instance = new Seriable(); private Seriable() {} public static Seriable getInstance() { return instance; } /** * jvm 实现 * @return */ private Object readResolve() { return instance; } }
test:
public class SeriableTest { public static void main(String[] args) { Seriable s1 = null; Seriable s2 = Seriable.getInstance(); FileOutputStream fos = null; try { fos = new FileOutputStream("seriable.obj"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(s2); oos.close(); fos.close(); ObjectInputStream ois = new ObjectInputStream( new FileInputStream("seriable.obj") ); s1 = (Seriable) ois.readObject(); System.out.println(s1 == s2);
} catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
2、优缺点
2.1 、优点:
》减少了内存开支,
》减少了系统的性能开销,当一个对象需要较多资源时,则可以启动时直接产生。 spring IOC
2.2、缺点
》 单例模式一般没有接口,扩展困难
》单例模式对测试不利。在并行环境中,如果单例没有完成,是不能进行测试的,没有接口也不能mock的方式虚拟一个对象。??why
》与单一职责有冲突。
2.3 使用场景
》项目中需要一个共享数据或共享访问点,如web页面中计数器
》 创建一个对象需要消耗的资源过多,如 访问IO和数据库等资源
》工具类
参考资料:
咕泡学院
《设计模式之蝉》
《大话设计模式》