介绍

核心作用

保证一个类只有一个实例,并且对外提供一个访问该实例的全局访问点。

优点

  • 由于单例模式只生成一个实例,减少系统性能开销
  • 单例模式可以在系统设置全局访问点,优化环境共享资源访问

    常用应用场景

  1. windows 系统的任务管理器、回收站;
  2. 项目中读取配置文件的类,一般配置文件只需要读取一次,所以只需要一个读取配置文件的类;
  3. 网站的计数器
  4. 应用程序中日志应用,一般都是单例模式实现,由于共享的日志文件一直处于打开状态,只能有一个实例操作,否则不好追加
  5. 数据库的连接池设计
  6. 操作系统的文件系统
  7. 在 Spring 中,每个 bean 默认是单例,这样做的优点是 Spring 容器很好的管理
  8. 在 Servlet 编程中,每个 Servlet 也是单例
  9. 在 SpringMVC 中,控制器对象也是单例

    步骤

  10. 私有化构造方法

  11. 提供私有的静态的对象的实例属性
  12. 提供对外访问的静态方法

    五种单例模式

  13. 饿汉式(线程安全,调用效率高,但是不能延时加载)

  14. 懒汉式(线程安全,调用效率不高,但是可以延时加载)
  15. 双重检测锁式(由于 JVM 底层内部模型原因,偶尔会出现问题,不建议使用)
  16. 静态内部类式(线程安全,调用效率高,可以延时加载)
  17. 枚举单例(线程安全,调用效率高,不能延时加载,实现简单。由于 JVM 从根本上提供保障,避免通过反射和反序列化的漏洞)

    饿汉式

    ```java 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(){

    1. return instance;

    } } ```

    懒汉式

    ```java package singleton;

/**

  • 懒汉式单例模式
  • 延时加载,资源利用率高,并发情况下 synchronized 效率低
  • @author SIMBA1949
  • @date 2019/6/5 21:39 */ public class Singleton02 { // volatile 保证 instance 在所有线程中同步 private static volatile Singleton02 instance;

    private Singleton02() {

    1. // 私有化构造方法避免类在外部被实例化

    }

    public static synchronized Singleton02 getInstance(){

    1. if (null == instance){
    2. instance = new Singleton02();
    3. }
    4. return instance;

    } } ```

    双重检测锁式

    ```java package singleton;

/**

  • 双重检测锁单例模式
  • @author SIMBA1949
  • @date 2019/6/5 21:44 */ public class Singleton03 {

    private static volatile Singleton03 instance;

    private Singleton03() { }

    public static Singleton03 getInstance(){

    1. if (null == instance){
    2. synchronized (Singleton03.class){
    3. if (null == instance){
    4. instance = new Singleton03();
    5. }
    6. }
    7. }
    8. return instance;

    } } ```

    静态内部类式

    ```java package singleton;

/**

  • 静态内部类单例模式
  • 类初始化的时候不会将内部类一起初始化,只有调用的时候才会加载静态内部类,加载类时线程时安全的
  • INSTANCE 是 static final 修饰的,保证内存中只有一个实例存在
  • 兼备并发高效调用和延时加载的优势
  • @author SIMBA1949
  • @date 2019/6/5 21:54 */ public class Singleton04 {

    private Singleton04() { }

    private static class Singleton04Inter{

    1. public static final Singleton04 INSTANCE = new Singleton04();

    }

    public static Singleton04 getInstance(){

    1. return Singleton04Inter.INSTANCE;

    } } ```

    枚举单例

    ```java package singleton;

/**

  • @author SIMBA1949
  • @date 2019/6/5 22:01 / public enum Singleton05 { /*
    • 这个枚举元素 INSTANCE 本身就是单例对象 */ INSTANCE } ```

      避免反射创建多个对象

      ```java package singleton;

/**

  • 避免反射创建多个对象,在构造方法中判断如果实例属性不为 null 时,抛出异常即可 *
  • @author SIMBA1949
  • @date 2019/6/5 22:08 */ public class Singleton064Reflect {

    private static Singleton064Reflect instance = new Singleton064Reflect();

    private Singleton064Reflect() {

    1. if (null != instance){
    2. throw new RuntimeException();
    3. }

    }

    public static Singleton064Reflect getInstance(){

    1. return instance;

    } } ```

    避免反序列化创建多个对象

    ```java 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(){

    1. 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);

  1. oos.writeObject(instance);
  2. // 反序列化
  3. ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("T:/s.java")));
  4. Singleton074Serializable deInstance = (Singleton074Serializable) ois.readObject();
  5. System.out.println(instance == deInstance);
  6. }

} ```