Bootstrap

详解:单例模式中的饿汉式和懒汉式

        单例模式是一种常用的设计模式,其目的是确保一个类只有一个实例(对象),并提供一个全局访问点。单例模式有两种常见的实现方式:饿汉式和懒汉式。

一、饿汉式

        饿汉式在类加载时就完成了实例化。因为类加载是线程安全的,所以在多线程环境下也是安全的。这种方式比较简单,但是由于实例在类加载时就创建,即使没有被使用,也会占用内存资源。

步骤:

1.私有化构造函数

解释:当私有化构造函数那么其他类就不能创建该类的对象,相当于该类的一切非静态的成员在其他类中都访问不了了!!!当你使用new创建对象时会调用其构造方法,因为构造函数私有化了,所以创建不了对象,创建不了对象相当于一切非静态的成员在其他类中都访问不了。

2.创建私有的静态类的对象

解释:为什么是私有的?防止其他类直接调用对象从而破坏单例的唯一性。为什么是静态的?如果不是静态的属性,那么其他类无法创建对象进行访问。

3.创建公共的静态get方法获取静态类的对象!!!

解释:该操作类似于封装私有的成员变量然后只能通过公共的set和get方法来获取,但是改操作是一个静态的方法,静态的方法只能调用静态的成员变量不能直接调用非静态的成员。

代码示例:

public class Person {
    private String name;
    // 在类加载时就创建实例
    private static Person person = new Person("小明");
    // 私有构造函数,防止外部实例化
    private Person(String name){
        this.name=name;
    }
    // 提供全局访问点  
    public static Person getInstance(){
        return person;
    }

    @Override
    public String toString() {
        return name;
    }
}
class Test{
    public static void main(String[] args) {
      Person person1 = Person.getInstance();
      Person person2 = Person.getInstance();
      System.out.println(person1);
      System.out.println(person2);
      System.out.println(person1==person2);
    }
}

结果如下:

小明
小明
true

         解释:这段代码,我们只能通过静态的getInstance()方法来获取Person对象,并且只能获取一个对象,因为在其他类(比如Test类)创建不了Person对象而只能获取一个Person对象,当我们再次调用静态的getInstance()方法来获取Person对象时,本质上是获取同一个对象,所以通过“==”进行比较得到的结果是true。

疑问1:为什么输出的是小明?

答:因为Person类重写了toString方法。

疑问2:为什么name属性不是静态的属性?

答:在你不需要调用name属性时不需要写成静态的属性。

疑问3:为什么实例(对象)在类加载时就创建?

答:因为在类加载时会进行静态属性的初始化和执行静态代码块。

参考文献:类什么时候加载?-CSDN博客

优点:

  • 线程安全:因为实例在类加载时就创建,天然线程安全。
  • 实现简单:代码量少,容易理解。

缺点:

  • 资源浪费:如果实例从未被使用,仍然会占用内存。

二、懒汉式

        懒汉式在第一次调用getInstance()方法时才创建实例。这种方式延迟了实例化,节省了资源,但需要考虑线程安全问题。

代码如下:

public class Person {
    private String name;
    // 声明实例,但不立即创建  
    private static Person person;
    // 私有构造函数,防止外部实例化
    private Person(String name){
        this.name=name;
    }
    // 提供全局访问点但非线程安全
    public static Person getInstance(){
        if (person==null){
            person = new Person("小明");
        }
        return person;
    }

    @Override
    public String toString() {
        return name;
    }
}
class Test{
    public static void main(String[] args) {
      Person person1 = Person.getInstance();
      Person person2 = Person.getInstance();
      System.out.println(person1);
      System.out.println(person2);
      System.out.println(person1==person2);
    }
}

结果如下(和饿汉式代码的结果一样):

小明
小明
true

解释:与饿汉式的区别是懒汉式在第一次调用getInstance()方法时才创建实例并不是类加载时就创建实例对象,创建实例对象时首先判断对象是否存在,不存在则创建对象,存在则返回已存在的对象。

线程安全问题

        在懒汉式单例模式中,如果多个线程同时调用getInstance()方法,并且此时实例尚未创建(即instance变量为null),那么这些线程都可能进入创建实例的代码块。如果没有适当的同步机制,就可能导致多个线程同时创建实例,从而违反单例模式的原则。

优点:

  • 延迟实例化,节省资源。

缺点:

  • 实现复杂:需要考虑线程安全问题。

        饿汉式和懒汉式的选择取决于具体的应用场景。如果单例对象较大且创建过程耗时,或者类加载时间较长,可以考虑使用懒汉式。如果单例对象较小且创建过程简单,或者类加载时间可以接受,可以考虑使用饿汉式。同时,懒汉式需要考虑线程安全问题,而饿汉式则不需要。

;