【不就是Java设计模式吗】解密单例八种设计模式,带你整理代码思路

简介: 【不就是Java设计模式吗】解密单例八种设计模式,带你整理代码思路

🧡前言

先看看思维导图:

image.png


📕在学习Java基础的时候,曾天真的以为单例设计模式就是懒汉式和饿汉式这两种。今天在B站学习了单例设计模式,真是打开了新世界的大门,自己总结了单例模式的学习笔记,从代码到自己的思路,最后到每一种方式的小结,相信小伙伴们看了后会有所收获


一、🍂模式介绍

所谓单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类

只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)


二、🍂分类

🏳‍🌈分类1:饿汉式

静态常量饿汉式

代码

public class Test01 {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        //结果是true,说明是同一个对象实例
        System.out.println(instance1 == instance2);
    }
}
class Singleton {
    //构造器私有化,外部不能通过new的方式创建此类的对象
    private Singleton() {}
    //本类内部创建对象实例
    private final static Singleton instance = new Singleton();
    //提供静态方法,得到实例对象
    public static Singleton getInstance() {
        return instance;
    }
}


🔥思路

🚩先创建私有构造器,然后创建私有的final的静态对象,最后提供一个可以得到实例对象的方法


结论与分析

线程安全,写法简单,该方式可用,在类装载的时候就完成了实例化。也就是因为在类装载的时候完成了实例化,所以,如果从始至终都从未使用过这个实例,就造成了内存的浪费


静态代码块饿汉式

代码

class Singleton {
    private Singleton() {}
    private static Singleton instance;
    static {
        instance = new Singleton();
    }
    public static Singleton getInstance() {
        return instance;
    }
}


🔥思路

🚩先创建私有类构造器,然后创建私有静态类引用,接着在静态代码块中给引用创建实例,最后提供一个可以得到实例对象的方法


结论与分析

线程安全,写法简单,该方式可用,在类装载的时候就完成了实例化。也就是因为在类装载的时候完成了实例化,所以,如果从始至终都从未使用过这个实例,就造成了内存的浪费


🏳‍🌈分类2:懒汉式

方式1

代码

class Singleton{
    private Singleton(){}
    private static Singleton instance;
    //当使用该方法时,才去创建对象
    public static Singleton getInstance(){
        if (instance == null)
            instance = new Singleton();
        return instance;
    }
}


🔥思路

🚩跟前面的饿汉式思路差不多,先创建一个私有类构造器,然后提供私有静态引用,最后写一个静态方法用来得到引用的对象实例;其中,引用的对象实例是用if (instance == null)来判断是否赋值的


结论与分析

线程不安全,当在多线程下,一个线程进入了if (instance == null)判断语句,还未来得及往下执行,另一个线程也通过了这个判断语句,这时候就可能产生多个实例,不建议使用


方式2

代码

class Singleton{
    private static Singleton instance;
    private Singleton(){}
    //加入同步处理的代码,解决线程安全问题
    public static synchronized Singleton getInstance(){
        if (instance == null)
            instance  = new Singleton();
        return instance;
    }
}


🔥思路

🚩思路跟方式1就是在方法中多了一个synchronized,可以保证线程安全


结论与分析

线程安全,但是效率低。每个线程在想获得类的实例的时候,执行getInstance()都要进行同步,其实这个方法只执行一次实例化代码就可以了,不推荐使用


方式3

代码

class Singleton{
    private static Singleton instance;
    private Singleton(){}
    public static Singleton getInstance(){
        if (instance  == null){
            synchronized(Singleton.class){
                instance  = new Singleton();
            }
        }
        return instance;
    }
}


🔥思路

🚩跟前面的代码没有很大区别,不同的是,方式3把synchronized线程同步问题放在getInstance()里面,但是并没有解决安全问题


结论与分析

线程不安全,效率也低,不推荐使用


🏳‍🌈分类3:双重检查

代码

class Singleton{
 /*
    volatile的作用:
    1、保证了不同线程对该变量操作的内存可见性(当一个线程修改了变量,其他使用次变量的线程可以立即知道这一修改)
    2、禁止了指令重排序.
     */
    private static volatile Singleton instance;
    private Singleton(){}
    public static Singleton getInstance(){
        if (instance == null){
            synchronized(Singleton.class){
                if (instance == null)
                    instance = new Singleton();
            }
        }
        return instance;
    }
}



🔥思路

🚩这里可以和懒汉式的方式3对比一下,大体上比方式3多了一层if语句的判断


总结分析

线程安全,效率较高;如代码所示,进行了两次if (instance == null)检查,可以保证线程安全,推荐使用


🏳‍🌈分类4:静态内部类

代码

class Singleton{
    private Singleton(){}
    private static class SingletonInstance{
        private static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance(){
        return SingletonInstance.INSTANCE;
    }
}


🔥思路

🚩这个思路也比较好理解,先构造私有类构造器方法,然后创建静态内部类用来创建类的对象,最后创建一个从内部类获取对象实例的方法


总结分析

线程安全,采用了类装载的机制来保证初始化实例时只有一个线程,效率高;静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化,推荐使用


🏳‍🌈分类5:枚举

代码

public class Test08 {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.INSTANCE;
        Singleton instance2 = Singleton.INSTANCE;
        //true
        System.out.println(instance1 == instance2);
        //运行结果:你好呀,我是卷心菜~~
        instance1.sayHello();
    }
}
enum Singleton{
    INSTANCE;
    public void sayHello(){
        System.out.println("你好呀,我是卷心菜~~");
    }
}



🔥思路

🚩枚举类真的是单例模式中最简单的一个了,就是根据自己需要的来写相应的属性和方法


总结分析

线程安全,能防止反序列化重新创建新的对象,非常推荐使用


三、🍂使用场景

🚩需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等)


四、🍂总结

单例模式保证了 系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能

当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new来获取对象


相关文章
|
5天前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
22 3
|
1月前
|
Java
java小工具util系列4:基础工具代码(Msg、PageResult、Response、常量、枚举)
java小工具util系列4:基础工具代码(Msg、PageResult、Response、常量、枚举)
52 24
|
13天前
|
前端开发 Java 测试技术
java日常开发中如何写出优雅的好维护的代码
代码可读性太差,实际是给团队后续开发中埋坑,优化在平时,没有那个团队会说我专门给你一个月来优化之前的代码,所以在日常开发中就要多注意可读性问题,不要写出几天之后自己都看不懂的代码。
51 2
|
27天前
|
Java 编译器 数据库
Java 中的注解(Annotations):代码中的 “元数据” 魔法
Java注解是代码中的“元数据”标签,不直接参与业务逻辑,但在编译或运行时提供重要信息。本文介绍了注解的基础语法、内置注解的应用场景,以及如何自定义注解和结合AOP技术实现方法执行日志记录,展示了注解在提升代码质量、简化开发流程和增强程序功能方面的强大作用。
66 5
|
27天前
|
存储 算法 Java
Java 内存管理与优化:掌控堆与栈,雕琢高效代码
Java内存管理与优化是提升程序性能的关键。掌握堆与栈的运作机制,学习如何有效管理内存资源,雕琢出更加高效的代码,是每个Java开发者必备的技能。
53 5
|
28天前
|
设计模式 消息中间件 搜索推荐
Java 设计模式——观察者模式:从优衣库不使用新疆棉事件看系统的动态响应
【11月更文挑战第17天】观察者模式是一种行为设计模式,定义了一对多的依赖关系,使多个观察者对象能直接监听并响应某一主题对象的状态变化。本文介绍了观察者模式的基本概念、商业系统中的应用实例,如优衣库事件中各相关方的动态响应,以及模式的优势和实际系统设计中的应用建议,包括事件驱动架构和消息队列的使用。
|
29天前
|
Java API 开发者
Java中的Lambda表达式:简洁代码的利器####
本文探讨了Java中Lambda表达式的概念、用途及其在简化代码和提高开发效率方面的显著作用。通过具体实例,展示了Lambda表达式如何在Java 8及更高版本中替代传统的匿名内部类,使代码更加简洁易读。文章还简要介绍了Lambda表达式的语法和常见用法,帮助开发者更好地理解和应用这一强大的工具。 ####
|
26天前
|
安全 Java API
Java中的Lambda表达式:简化代码的现代魔法
在Java 8的发布中,Lambda表达式的引入无疑是一场编程范式的革命。它不仅让代码变得更加简洁,还使得函数式编程在Java中成为可能。本文将深入探讨Lambda表达式如何改变我们编写和维护Java代码的方式,以及它是如何提升我们编码效率的。
|
安全 Java 双11
Java单例类的9种写法
一. 什么是单例模式 因进程需要,有时我们只需要某个类同时保留一个对象,不希望有更多对象,此时,我们则应考虑单例模式的设计。 ​ 二. 单例模式的特点 1、单例模式只能有一个实例。 ​ 2、单例类必须创建自己的唯一实例。
1320 0
|
3天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者