Java 设计模式以虹猫蓝兔的故事讲解单例模式

 更新时间:2022年03月28日 17:27:53   作者:桃花键神  
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式

专栏介绍

【JAVA长虹键法】 主要讲了23种设计模式,本系列专栏会以虹猫蓝兔七侠传的故事为例来给大家详细分析所有模式,希望能给大家带来帮助!

本期介绍

模式: 单例模式

案例: 虹猫蓝兔造剑

什么是单例模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

注意:

1、单例类只能有一个实例。

2、单例类必须自己创建自己的唯一实例。

3、单例类必须给所有其他对象提供这一实例。

单例模式大致分为懒汉式和饿汉式,接下来用案例分析

懒汉式一

是否 Lazy 初始化: 是

是否多线程安全:否

实现难度: 易

描述: 这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。

这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。

概念看不懂没关系,接下来举例。

案例一:

创建一个剑类,这个类可以实例化一把剑。

虹猫和蓝兔两个人都想要造一把剑,虹猫先打造了一把剑,命名为长虹剑,然后蓝兔也打造了一把剑,但是没有命名。

现在来分析两个情况,一个情况是正常模式,另一种情况是单例模式。

正常模式

剑类:

public class Jians {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

测试类:

public class Demo1 {
    public static void main(String[] args) {
         //虹猫的剑
        Jians hong = new Jians();
         //蓝兔的剑
        Jians lan = new Jians();
        hong.setName("长虹剑");
        System.out.println(hong.getName());
        System.out.println(lan.getName());
    }
}

结果:

在正常模式下,我new了虹猫和蓝兔的剑。其实就是两把剑,两个不同的对象。

单例模式

剑类:

剑类中的getInstance()方法有一个造剑的功能,也就是new一个剑对象的功能。

 public class Jian {
    private static Jian jian;
    private String name;

    public static Jian getInstance() {
        if (jian == null) {
            jian = new Jian();
        }
        return jian;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

测试类:

public class Demo {
    public static void main(String[] args) {
        //虹猫的剑
        Jian hong = Jian.getInstance();
        //蓝兔的剑
        Jian lan = Jian.getInstance();
        //虹猫把剑命名长虹剑
        hong.setName("长虹剑");
        //输出
        System.out.println(hong.getName());
        System.out.println(lan.getName());
    }
}

结果:

在单例模式下,我new了虹猫和蓝兔的剑。其实就是一把剑,一个相同的对象。

为什么线程不安全呢

单例模式下的剑类:

 public class Jian {
    private static Jian jian;
    private String name;

    public static Jian getInstance() {
        if (jian == null) {
            jian = new Jian();
        }
        return jian;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

就拿这个类来说,他是线程不安全的。因为他通过getInstance()方法来获取对象。如果是多线程运行,有线程1和线程2都先后进入了这个方法,因为线程1刚进入方法还没有返回对象,线程2就进入了方法。所以线程2也会new一个对象,因为此时线程2进入方法的时候jian还是null的。

懒汉式二

是否 Lazy 初始化: 是

是否多线程安全: 是

实现难度: 易

描述: 这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。

优点: 第一次调用才初始化,避免内存浪费。

缺点: 必须加锁 synchronized 才能保证单例,但加锁会影响效率。

为什么线程安全呢

这里还用上面那个案例,这次主要介绍懒汉式一和懒汉式二的区别。

懒汉式一和懒汉式二的主要区别在剑类上。

懒汉式一的剑类:

 public class Jian {
    private static Jian jian;
    private String name;

    public static Jian getInstance() {
        if (jian == null) {
            jian = new Jian();
        }
        return jian;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

懒汉式二的剑类:

 public class Jian {
    private static Jian jian;
    private String name;

    public static synchronized Jian getInstance() {
        if (jian == null) {
            jian = new Jian();
        }
        return jian;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

在getInstance()方法前加了个字段synchronized,synchronized是一把锁,作用是同一时间只能有一个线程进入这个方法,这样就避免了懒汉式一中两个线程都进入方法的情况出现,就不会new两个对象,所以线程安全。

饿汉式

是否 Lazy 初始化: 否

是否多线程安全: 是

实现难度: 易

描述: 这种方式比较常用,但容易产生垃圾对象。

优点: 没有加锁,执行效率会提高。

缺点: 类加载时就初始化,浪费内存。

饿汉式就是直接在类中new一个对象,就是不管你需不需要剑我已经把剑造好了。

饿汉式剑类:

public class Jian {
    private static Jian jian=new Jian();
    private String name;

    public static synchronized Jian getInstance() {
        return jian;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

测试类:

public class Demo {
    public static void main(String[] args) {
        //虹猫的剑
        Jian hong = Jian.getInstance();
        //蓝兔的剑
        Jian lan = Jian.getInstance();
        //虹猫把剑命名长虹剑
        hong.setName("长虹剑");
        //输出
        System.out.println(hong.getName());
        System.out.println(lan.getName());
    }
}

饿汉式中剑类的getInstance()方法已经失去了造剑的功能,测试类调用它只是返回一把提前造好的剑

懒汉式与饿汉式的区别

还是这个案例,虹猫和蓝兔都想造一把剑,懒汉式是有一个剑类,剑类中有一个造剑的方法,调用这个方法的时候打造一把剑。饿汉式也有一个剑类,不同的是这个剑类直接就把剑造好了,没有造剑的方法,不管你虹猫和蓝兔想不想造剑,剑都已经造好了。

优点: 没有加锁,执行效率会提高。

缺点: 类加载时就初始化,浪费内存。

现在在看饿汉式的优缺点就容易理解了。

下期预告

模式: 简单工厂模式

案例: 虹猫蓝兔莎莉找铸剑师造剑

到此这篇关于Java 设计模式以虹猫蓝兔的故事讲解单例模式的文章就介绍到这了,更多相关Java 单例模式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • IDEA+Maven创建Spring项目的实现步骤

    IDEA+Maven创建Spring项目的实现步骤

    这篇文章主要介绍了IDEA+Maven创建Spring项目的实现步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • Gradle的基本使用

    Gradle的基本使用

    这篇文章主要介绍了Gradle的基本使用方法,帮助大家更好的理解和学习Gradle的相关知识,感兴趣的朋友可以了解下
    2021-03-03
  • 关于Eureka的概念作用以及用法详解

    关于Eureka的概念作用以及用法详解

    这篇文章主要介绍了关于Eureka的概念作用以及用法详解,服务治理就是提供了微服务架构中各微服务实例的快速上线或下线且保持各服务能正常通信的能力的方案总称,需要的朋友可以参考下
    2023-05-05
  • 使用SpringCache进行缓存数据库查询方式

    使用SpringCache进行缓存数据库查询方式

    这篇文章主要介绍了使用SpringCache进行缓存数据库查询方式,具有很好的参考价值,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • Java回调机制解读

    Java回调机制解读

    本文主要介绍了Java回调机制的相关知识,具有很好的参考价值,下面跟着小编一起来看下吧
    2017-02-02
  • Java并发容器ConcurrentLinkedQueue解析

    Java并发容器ConcurrentLinkedQueue解析

    这篇文章主要介绍了Java并发容器ConcurrentLinkedQueue解析,
    2023-12-12
  • Spring中的依赖注入DI详解

    Spring中的依赖注入DI详解

    这篇文章主要介绍了Spring中的依赖注入DI详解,组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将依赖关系注入到组件之中,依赖注入的目的并非为软件系统带来更多功能,是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台,需要的朋友可以参考下
    2024-01-01
  • Storm框架整合springboot的方法

    Storm框架整合springboot的方法

    Storm框架中的每个Spout和Bolt都相当于独立的应用,Strom在启动spout和bolt时提供了一个open方法(spout)和prepare方法(bolt)。这篇文章主要介绍了Storm框架整合springboot的方法,需要的朋友可以参考下
    2018-11-11
  • Springboot使用Spring Data JPA实现数据库操作

    Springboot使用Spring Data JPA实现数据库操作

    Spring Data JPA 是 Spring 基于 Spring Data 框架、在JPA 规范的基础上开发的一个框架,使用 Spring Data JPA 可以极大地简化JPA 的写法,本章我们将详细介绍在Springboot中使用 Spring Data JPA 来实现对数据库的操作
    2021-06-06
  • Spring5学习之基础知识总结

    Spring5学习之基础知识总结

    这篇文章主要介绍了Spring5学习之基础知识总结,文中有非常详细的代码示例,对正在学习java的小伙伴们有非常好的帮助,需要的朋友可以参考下
    2021-05-05

最新评论