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

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

一、饿汉式

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

步骤:

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),那么这些线程都可能进入创建实例的代码块。如果没有适当的同步机制,就可能导致多个线程同时创建实例,从而违反单例模式的原则。

优点:

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

缺点:

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

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

相关推荐
间彧1 小时前
Spring Cloud Gateway与Kong或Nginx等API网关相比有哪些优劣势?
后端
迎風吹頭髮1 小时前
UNIX下C语言编程与实践63-UNIX 并发 Socket 编程:非阻塞套接字与轮询模型
java·c语言·unix
间彧1 小时前
如何基于Spring Cloud Gateway实现灰度发布的具体配置示例?
后端
间彧1 小时前
在实际项目中如何设计一个高可用的Spring Cloud Gateway集群?
后端
我是华为OD~HR~栗栗呀1 小时前
23届考研-Java面经(华为OD)
java·c++·python·华为od·华为·面试
间彧1 小时前
如何为Spring Cloud Gateway配置具体的负载均衡策略?
后端
间彧1 小时前
Spring Cloud Gateway详解与应用实战
后端
武文斌771 小时前
项目学习总结:LVGL图形参数动态变化、开发板的GDB调试、sqlite3移植、MQTT协议、心跳包
linux·开发语言·网络·arm开发·数据库·嵌入式硬件·学习
Javatutouhouduan1 小时前
Java程序员如何深入学习JVM底层原理?
java·jvm·java面试·后端开发·java架构师·java程序员·互联网大厂
爱吃喵的鲤鱼1 小时前
仿mudou——Connection模块(连接管理)
linux·运维·服务器·开发语言·网络·c++