面试官:写几种你知道的单例模式!Java实现单例模式有几种方式?3,5?饿汉,懒汉!?

面试官:写几种你知道的单例模式!Java实现单例模式有几种方式?3,5?饿汉,懒汉!?

在面试中经常会遇到单例模式的问题,动不动就是你知道几种单例模式,请手写几种你知道的单例模式,为了能彻底厘清该问题,本文详细阐述了单例模式的最全的八种写法以及茴香豆的茴字的四种写法(手动狗头),并比较其存在的优劣,如果本文对你有所帮助的话点赞哦亲。

  • 什么是设计模式(Design Pattern)?

    设计模式是人类在解决各类问题时所总结出来的有用的经验,它不是软件工程中特有的概念。具体来说设计模式是一种思想,是解决某类问题的通用方案,代表了最佳实践,也是前人经过相当长时间的试验和错误而分析总结出来的。

  • 分类:

    设计模式总共分为三类,总共23

    • 创建型 :单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式
    • 结构型 :适配器模式、桥接模式、装饰者模式、组合模式、外观模式、享元模式、代理模式
    • 行为型 : 模板方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式(责任链模式)

正文开始

  • 单例模式

    单例模式就是通过某种实现方式使得在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得该对象实例的静态方法。

    应用场景:

    ​ 1、比如Windows的资源管理器,同一台Windows上不能同时打开两个资源管理器。

    ​ 2、数据库连接池,因为数据库连接是一种数据库资源,为了节省打开或者关闭数据库连接所引起的损耗,单例模式的使用就尤为重要。

    ​ 3、Spring对bean的管理,可以通过scope选择该bean是单例(singleton)还是多例(prototype)。

    综上所述,单例模式在软件工程中使用还是比较频繁的,为了能在面试中能够脱颖而出让面试官中意你,并且工作中遇到能熟练运用,下面主要通过代码的方式来详细介绍单例模式的八种实现方式。

    单例模式的八种实现方式

    1. 饿汉式(静态常量)
    2. 饿汉式(静态代码块)
    3. 懒汉式(线程不安全)
    4. 懒汉式(线程安全,同步方法实现)
    5. 懒汉式(线程安全,同步代码块实现)
    6. 双重检查(Double Check)
    7. 静态内部类
    8. 枚举
  • Java实现

1. 饿汉式(静态常量)

​ 步骤:

​ ①、私有化构造器,防止外部通过new生成实例

​ ②、提供一个同类型的常量,用于接收内部实例

​ ③、提供一个public方法供外部调用以获取生成的实例

public class Singleton {

    //1、私有化构造器,防止外部new产生实例
    private Singleton() {

    }

    //2、定义一个Singleton类型的常量,用于接收Singleton内部实例
    private final static Singleton instance = new Singleton();

    //3、定义一个public方法提供给外部调用,获取实例对象
    public static Singleton getInstance() {
        return instance;
    }
}
  • 优点:

    实现较为简单,定义为常量即在类加载的时候就完成实例化。通过ClassLoader机制避免了线程安全的问题。

  • 缺点:

    根据JVM加载类的方式得知类加载器并不需要等到某个类被主动使用时才加载,JVM允许类加载器在预料到某个类将要被使用时就预先加载它,所以类加载是一种不确定行为,而这种饿汉式的实现方式会在类加载的时候就完成实例化,所以不能达到懒加载(Lazy Loading)的效果。可能造成内存浪费。

2. 饿汉式(静态代码块)

​ 步骤:

​ ①、私有化构造器,防止外部通过new生成实例

​ ②、定义static实例对象,用于接收静态代码块生成的内部实例

​ ③、通过静态代码块创建对象实例

​ ④、提供一个public方法供外部调用以获取生成的实例

public class Singleton {
    //1、私有化构造器,防止外部new产生实例
    private Singleton(){

    }

    //2、定义实例对象准备接收
    private static Singleton instance;

    //3、通过静态代码块创建对象实例
    static {
        instance = new Singleton();
    }

    //4、定义一个public方法提供给外部调用,获取实例对象
    public static Singleton getInstance(){
        return instance;
    }
}
  • 该方法优缺点同上(可能造成内存浪费

3. 懒汉式(线程不安全)

​ 步骤:

​ ①、私有化构造器,防止外部通过new生成实例

​ ②、定义static实例对象,用于接收静态代码块生成的内部实例

​ ③、提供一个public方法供外部调用以获取生成的实例,首先判断该实例是否已经生成,如果已经生成则直接使用,否则再实例化生成。

  public class Singleton {
  
      //1、私有化构造器,防止外部new产生实例
      private Singleton(){
          
      }
      
      //2、定义一个static实例对象接收对象实例
      private static Singleton instance;
      
      //3、定义一个public方法提供给外部调用,获取实例对象
      public static Singleton getInstance(){
          if (null == instance)
              instance = new Singleton();
          return instance;
      }
  
  }
  • 优点:

    实现简单且可以实现懒加载(Lazy Loading)

  • 缺点:

    if (null == instance)该句代码在多线程环境存在线程不安全问题,可能生成多个实例,破坏了单例模式。实际开发中不要使用这种方式。

4. 懒汉式(线程安全,同步方法实现)

​ 步骤:

​ ①、私有化构造器,防止外部通过new生成实例

​ ②、定义static实例对象,用于接收静态代码块生成的内部实例

​ ③、提供一个public方法供外部调用以获取生成的实例并通过synchronized加锁,首先判断该实例是否已经生成,如果已经生成则直接使用,否则再实例化生成。

  public class Singleton {
  
      //1、私有化构造器,防止外部new产生实例
      private Singleton(){
          
      }
      
      //2、定义一个static实例对象接收对象实例
      private static Singleton instance;
      
      //3、定义一个public方法提供给外部调用,通过synchronized加锁,获取实例对象
      public static synchronized Singleton getInstance(){
          if (null == instance)
              instance = new Singleton();
          return instance;
      }
  
  }
  • 优点:

    解决了懒汉式了线程安全的问题。

  • 缺点:

    由于通过synchronized加锁,多线程环境下获取对象实例效率极低。实际开发不推荐使用。

5. 懒汉式(线程不安全,同步代码块实现)

​ 步骤:

​ ①、私有化构造器,防止外部通过new生成实例

​ ②、定义static实例对象,用于接收静态代码块生成的内部实例

​ ③、定义一个public方法提供给外部调用,通过synchronized同步代码块加锁,获取实例对象。

  public class Singleton {
  
      //1、私有化构造器,防止外部new产生实例
      private Singleton(){
          
      }
      
      //2、定义一个static实例对象接收对象实例
      private static Singleton instance;
      
      //3、定义一个public方法提供给外部调用,通过synchronized同步代码块加锁,获取实例对象
      public static Singleton getInstance(){
          if (null == instance)
              synchronized (Singleton.class){
           		instance = new Singleton();
            }
          return instance;
      }
  
  }
  • 缺点:

    通过synchronized代码块加锁,依然线程不安全。实际开发不推荐使用。

6. 双重检查

​ 步骤:

​ ①、私有化构造器,防止外部通过new生成实例

​ ②、定义volatile实例对象,保证生成实例对象的可见性,并用于接收静态代码块生成的内部实例

​ ③、定义一个public方法提供给外部调用,获取实例对象,同时再在内部进行两次判断,第二次进行synchronized代码块加锁,一旦实例化成功,锁外的线程即可通过volatile获得实例。

  public class Singleton {
    
    //1、私有化构造器,防止外部new产生实例
    private Singleton(){

    }

    //2、定义volatile实例对象,保证数据可见性
    private static volatile Singleton instance;
    
    //3、定义一个public方法提供给外部调用,获取实例对象,同时再在内部进行进行synchronized代码块加锁
    public static Singleton getInstance(){
        if (null == instance)
            synchronized (Singleton.class){
                if (null == instance)
                    instance = new Singleton();
            }
        return instance;
    }

}
  • 优点:

    实现了懒加载(Lazy Loading

    双重检查,保证线程安全

    实例化代码只用执行一次,多线程后面再次访问时可通过volatile通知。

推荐使用。

7. 静态内部类

​ 步骤:

​ ①、私有化构造器,防止外部通过new生成实例

​ ②、通过静态内部类的属性获得Singleton实例

​ ③、定义一个public方法提供给外部调用,获取实例对象。

  public class Singleton {

    //1、私有化构造器,防止外部new产生实例
    private Singleton(){

    }

    //2、通过静态内部类的属性获得Singleton实例
    private static class SingletonInstance{
        private static Singleton INSTANCE = new Singleton();
    }

    //3、定义一个public方法提供给外部调用,获取实例对象
    public static Singleton getInstance(){
        return SingletonInstance.INSTANCE;
    }

}
  • 优点:

    实现了懒加载(Lazy Loading

    只有当getInstance()方法第一次被调用时,才会去导致虚拟机加载SingletonInstance类,而类的静态属性只会在第一次加载类的时候初始化,所以这种方式通过JVM的加载机制以保证线程安全。

推荐使用。

8. 枚举

​ 步骤:

​ ①、通过枚举类属性实现

  public enum Singleton {
    INSTANCE;
    public void hello(){
        System.out.println("hello");
    }
}
  • 优点:

    线程安全,实现简单

    防止反序列化重新创建新的对象

强烈推荐使用。

总结

单例模式保证了 系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以极大地提高系统性能,例如数据库连接池,session工厂等。

回、囘、囬、廻

你可能感兴趣的:(设计模式)