-
单例模式(Singleton)
-
创建型
-
保证从系统启动到系统终止,全过程只会产生一个实例,并提供一个访问它的全局访问点
-
初衷是使资源共享,只需要初始化一次,所有人都可以重复使用
-
当我们在应用中遇到功能性冲突的时候,需要使用单例模式
-
Spring 没有从构造器级别去控制单例,因为 Spring 管理的是任意的 Java 对象
-
Spring 下默认的 Bean 均为单例
-
解决并发访问时候的线程安全问题,序列化与反序列化的时候出现多例
-
构造方法私有化
-
生活场景
- 配置信息Config
- 日历Calender
- IOC 容器
- Listener
-
模式
-
饿汉式
- 在类加载的时候就立即初始化,并且创建单例对象。
- 优点
- 没有加锁,执行效率高。
- 用户体验好。
- 线程安全,线程还没创建之前,就已经创建好实例。
- 缺点:占用资源
public class Hungry { private Hungry(){ } private static final Hungry hungry = new Hungry(); public static Hungry getInstance(){ return hungry; } }
-
懒汉式
- 默认在加载的时候不实例化,在第一次使用的时候才实例化。
- 线程不安全。
- Spring 延时加载。
/** * 线程不安全 **/ public class Lazy { private Lazy(){} private static Lazy lazy; public static Lazy getInstance(){ if(lazy == null) lazy = new Lazy(); return lazy; } } /** * 线程安全,效率低 **/ public class Lazy2 { private Lazy2(){} private static Lazy2 lazy; public static synchronized Lazy2 getInstance(){ if(lazy == null) lazy = new Lazy2(); return lazy; } } /** * 在外部类被调用时内部类才加载,避免了线程安全问题 * 这种形式兼顾饿汉式的内存浪费,也兼顾 synchronized 性能问题,完美的屏蔽了这两个缺点 * 比较完美的单例模式实现方式 **/ public class Lazy3 { private static boolean initialized = false; private Lazy3(){ /** * 避免反射入侵 */ if (initialized) throw new RuntimeException("请使用 Lazy3.getInstance() 获取实例对象。"); initialized = true; } /** * static:空间共享 * final:保证方法不会被重写,重载 * 在返回结果以前一定会先加载内部类 */ public static final Lazy3 getInstance(){ return LazyHolder.LAZY; } private static class LazyHolder{ private static final Lazy3 LAZY = new Lazy3(); } }
-
注册式
- 首次使用在固定容器中注册,并且将使用过的对象进行缓存,下次取对象直接从缓存中取值,以保证每次获取的都是同一个对象。
- IOC 中的单例模式
public class BeanFactory { private BeanFactory(){} // 线程安全 private static final Map<String,Object> ioc = new ConcurrentHashMap<>(); public static Object getInstance(String className){ if(ioc.containsKey(className)) return ioc.get(className); // Class.forName(className).newInstance()在 jdk1.9 中弃用 try { Constructor<?> clazz = Class.forName(className).getDeclaredConstructor(); clazz.setAccessible(true); ioc.put(className,clazz.newInstance()); return ioc.get(className); } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | ClassNotFoundException e) { e.printStackTrace(); throw new RuntimeException("Bean 不存在于 IOC 中"); } } } /** * 注册枚举,常量中去使用 **/ public enum ColorEnum { WHITE(){ private int r = 255; private int g = 255; private int b = 255; },BLACK(){ private int r = 0; private int g = 0; private int b = 0; }; }
-
序列化:重写 readResolve() 保证单例
/** * 防止反序列化时导致单例破坏 * 序列化:把内存中的状态通过转换成字节码的形式(IO 流),写入到其他地方,内存中状态被永久保存 * 反序列化:将已经持久化的字节码内容转换为 IO 流,通过 IO 流将读取内容转换为 Java 对象 * 在转换过程中会重新创建对象 */ public class Serial implements Serializable { public final static Serial INSTANCE = new Serial(); private Serial(){} public static Serial getInstance(){ return INSTANCE; } private Object readResolve(){ return INSTANCE; } }
-
-