二十三种设计模式之单例模式
核心作用
保证一个类只有一个实例,并且对外提供一个访问该实例的全局访问点。
优点
- 由于单例模式只生成一个实例,减少系统性能开销
- 单例模式可以在系统设置全局访问点,优化环境共享资源访问
常用应用场景
- windows 系统的任务管理器、回收站;
- 项目中读取配置文件的类,一般配置文件只需要读取一次,所以只需要一个读取配置文件的类;
- 网站的计数器
- 应用程序中日志应用,一般都是单例模式实现,由于共享的日志文件一直处于打开状态,只能有一个实例操作,否则不好追加
- 数据库的连接池设计
- 操作系统的文件系统
- 在 Spring 中,每个 bean 默认是单例,这样做的优点是 Spring 容器很好的管理
- 在 Servlet 编程中,每个 Servlet 也是单例
- 在 SpringMVC 中,控制器对象也是单例
步骤
- 私有化构造方法
- 提供私有的静态的对象的实例属性
- 提供对外访问的静态方法
五种单例模式
- 饿汉式(线程安全,调用效率高,但是不能延时加载)
- 懒汉式(线程安全,调用效率不高,但是可以延时加载)
- 双重检测锁式(由于 JVM 底层内部模型原因,偶尔会出现问题,不建议使用)
- 静态内部类式(线程安全,调用效率高,可以延时加载)
- 枚举单例(线程安全,调用效率高,不能延时加载,实现简单。由于 JVM 从根本上提供保障,避免通过反射和反序列化的漏洞)
饿汉式
package singleton; /** * 饿汉式单例模式 * 类加载时,立即加载这个对象。加载类是,天然的线程安全 * * @author SIMBA1949 * @date 2019/6/5 21:20 */ public class Singleton01 { private static Singleton01 instance = new Singleton01(); private Singleton01() { } public static Singleton01 getInstance(){ return instance; } }
懒汉式
package singleton; /** * 懒汉式单例模式 * 延时加载,资源利用率高,并发情况下 synchronized 效率低 * * @author SIMBA1949 * @date 2019/6/5 21:39 */ public class Singleton02 { private static Singleton02 instance; private Singleton02() { } public static synchronized Singleton02 getInstance(){ if (null == instance){ instance = new Singleton02(); } return instance; } }
双重检测锁单例模式
package singleton; /** * 双重检测锁单例模式 * * @author SIMBA1949 * @date 2019/6/5 21:44 */ public class Singleton03 { private static Singleton03 instance; private Singleton03() { } public static Singleton03 getInstance(){ if (null == instance){ synchronized (Singleton03.class){ if (null == instance){ instance = new Singleton03(); } } } return instance; } }
静态内部类单例模式
package singleton; /** * 静态内部类单例模式 * 类初始化的时候不会将内部类一起初始化,只有调用的时候才会加载静态内部类,加载类时线程时安全的 * INSTANCE 是 static final 修饰的,保证内存中只有一个实例存在 * 兼备并发高效调用和延时加载的优势 * * @author SIMBA1949 * @date 2019/6/5 21:54 */ public class Singleton04 { private Singleton04() { } private static class Singleton04Inter{ public static final Singleton04 INSTANCE = new Singleton04(); } public static Singleton04 getInstance(){ return Singleton04Inter.INSTANCE; } }
枚举单例模式
package singleton; /** * @author SIMBA1949 * @date 2019/6/5 22:01 */ public enum Singleton05 { /** * 这个枚举元素 INSTANCE 本身就是单例对象 */ INSTANCE }
避免反射创建多个对象
package singleton; /** * 避免反射创建多个对象,在构造方法中判断如果实例属性不为 null 时,抛出异常即可 * * @author SIMBA1949 * @date 2019/6/5 22:08 */ public class Singleton064Reflect { private static Singleton064Reflect instance = new Singleton064Reflect(); private Singleton064Reflect() { if (null != instance){ throw new RuntimeException(); } } public static Singleton064Reflect getInstance(){ return instance; } }
避免反序列化创建多个对象
package singleton; import java.io.*; /** * 添加 readResolve() 方法可以避免反序列化创建多个对象 * * @author SIMBA1949 * @date 2019/6/5 22:12 */ public class Singleton074Serializable implements Serializable{ private static final long serialVersionUID = -3093202203157151493L; private static Singleton074Serializable instance = new Singleton074Serializable(); private Singleton074Serializable() { } public static Singleton074Serializable getInstance(){ return instance; } /** * 添加 readResolve() 方法可以避免反序列化创建多个对象 * @return */ private Object readResolve(){ return instance; } } class DeserializableTest{ public static void main(String[] args) throws IOException, ClassNotFoundException { Singleton074Serializable instance = Singleton074Serializable.getInstance(); // 序列化 FileOutputStream fis = new FileOutputStream(new File("T:/s.java")); ObjectOutputStream oos = new ObjectOutputStream(fis); oos.writeObject(instance); // 反序列化 ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("T:/s.java"))); Singleton074Serializable deInstance = (Singleton074Serializable) ois.readObject(); System.out.println(instance == deInstance); } }