Java23种设计模式(二)

1、单例模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供了一个全局访问点来访问该实例。

  • 1、单例类只能有一个实例。
  • 2、单例类必须自己创建自己的唯一实例。
  • 3、单例类必须给所有其他对象提供这一实例。

1.1、应用实例

  • 一个班级只有一个班主任。
  • Windows 在多进程多线程环境下操作文件时,避免多个进程或线程同时操作一个文件,需要通过唯一实例进行处理。
  • 设备管理器设计为单例模式,例如电脑有两台打印机,避免同时打印同一个文件。

1.2、注意事项

  • 线程安全:getInstance() 方法中需要使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成实例被多次创建。
  • 延迟初始化:实例在第一次调用 getInstance() 方法时创建。
  • 序列化和反序列化:重写 readResolve 方法以确保反序列化时不会创建新的实例。
  • 反射攻击:在构造函数中添加防护代码,防止通过反射创建新实例。
  • 类加载器问题:注意复杂类加载环境可能导致的多个实例问题。

1.3、单例模式的几种实现方式

单例模式的实现有多种方式,如下所示:

1、懒汉式,线程不安全

  • 是否 Lazy 初始化:是
  • 是否多线程安全:否
  • 实现难度:易

描述:这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。

实例

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

2、懒汉式,线程安全

  • 是否 Lazy 初始化:是
  • 是否多线程安全:是
  • 实现难度:易

描述:这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。
优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)。

实例

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

3、饿汉式

  • 是否 Lazy 初始化:否
  • 是否多线程安全:是
  • 实现难度:易

描述:这种方式比较常用,但容易产生垃圾对象。
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。
它基于 classloader 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。

实例

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

4、双检锁/双重校验锁(DCL,即 double-checked locking)
JDK 版本:JDK1.5 起

  • 是否 Lazy 初始化:是
  • 是否多线程安全:是
  • 实现难度:较复杂

描述:这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
getInstance() 的性能对应用程序很关键。

实例

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
            if (singleton == null) {  
                singleton = new Singleton();  
            }  
        }  
    }  
    return singleton;  
    }  
}

5、登记式/静态内部类

  • 是否 Lazy 初始化:是
  • 是否多线程安全:是
  • 实现难度:一般

描述:这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
这种方式同样利用了 classloader 机制来保证初始化 instance 时只有一个线程,它跟第 3 种方式不同的是:第 3 种方式只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 lazy loading 效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。想象一下,如果实例化 instance 很消耗资源,所以想让它延迟加载,另外一方面,又不希望在 Singleton 类加载时就实例化,因为不能确保 Singleton 类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化 instance 显然是不合适的。这个时候,这种方式相比第 3 种方式就显得很合理。

实例

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

6、枚举
JDK 版本:JDK1.5 起

  • 是否 Lazy 初始化:否
  • 是否多线程安全:是
  • 实现难度:易

描述:这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。
不能通过 reflection attack 来调用私有构造方法。

实例

public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}

经验之谈:一般情况下,不建议使用第 1 种和第 2 种懒汉方式,建议使用第 3 种饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用第 5 种登记方式。如果涉及到反序列化创建对象时,可以尝试使用第 6 种枚举方式。如果有其他特殊的需求,可以考虑使用第 4 种双检锁方式。

2、建造者模式

建造者模式是一种创建型设计模式,它允许你创建复杂对象的步骤与表示方式相分离。

建造者模式是一种创建型设计模式,它的主要目的是将一个复杂对象的构建过程与其表示相分离,从而可以创建具有不同表示形式的对象。

将变与不变的部分分离开。

与工厂模式的区别是:建造者模式更加关注于零件装配的顺序。

2.1、结构

建造者模式包含以下几个主要角色:

  • 产品(Product):要构建的复杂对象。产品类通常包含多个部分或属性。
  • 抽象建造者(Builder):定义了构建产品的抽象接口,包括构建产品的各个部分的方法。
  • 具体建造者(Concrete Builder):实现抽象建造者接口,具体确定如何构建产品的各个部分,并负责返回最终构建的产品。
  • 指导者(Director):负责调用建造者的方法来构建产品,指导者并不了解具体的构建过程,只关心产品的构建顺序和方式。

2.2、应用实例

去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出不同的"套餐"。
Java 中的 StringBuilder。

3、原型模式

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式之一。

这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
优点

  • 性能提高
  • 避免构造函数的约束

缺点

  • 配备克隆方法需要全面考虑类的功能,对已有类可能较难实现,特别是处理不支持串行化的间接对象或含有循环结构的引用时。
  • 必须实现 Cloneable 接口。

4、适配器模式

适配器模式(Adapter Pattern)充当两个不兼容接口之间的桥梁,属于结构型设计模式。它通过一个中间件(适配器)将一个类的接口转换成客户期望的另一个接口,使原本不能一起工作的类能够协同工作。

这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。

假设有一个音频播放器,它只能播放 MP3 文件。现在,我们需要播放 VLC 和 MP4 文件,可以通过创建一个适配器来实现:

  • 目标接口:定义一个可以播放多种格式文件的音频播放器接口。
  • 适配者类:现有的音频播放器,只能播放 MP3 文件。
  • 适配器类:创建一个新的类,实现目标接口,并在内部使用适配者类来播放 MP3 文件,同时添加对 VLC 和 MP4 文件的支持。

4.1、实现方式

继承或依赖:推荐使用依赖关系,而不是继承,以保持灵活性。

4.2、优点

  • 促进了类之间的协同工作,即使它们没有直接的关联。
  • 提高了类的复用性。
  • 增加了类的透明度。
  • 提供了良好的灵活性。

4.3、缺点

过度使用适配器可能导致系统结构混乱,难以理解和维护。
在Java中,由于只能继承一个类,因此只能适配一个类,且目标类必须是抽象的。

5、桥接模式

桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。

这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类,这两种类型的类可被结构化改变而互不影响。

桥接模式的目的是将抽象与实现分离,使它们可以独立地变化,该模式通过将一个对象的抽象部分与它的实现部分分离,使它们可以独立地改变。它通过组合的方式,而不是继承的方式,将抽象和实现的部分连接起来。

5.1、主要解决的问题

避免使用继承导致的类爆炸问题,提供更灵活的扩展方式。

6、过滤器模式

过滤器模式(Filter Pattern)或标准模式(Criteria Pattern)是一种设计模式,这种模式允许开发人员使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来。这种类型的设计模式属于结构型模式,它结合多个标准来获得单一标准。

用于将对象的筛选过程封装起来,允许使用不同的筛选标准动态地筛选对象。

6.1、使用场景

  • 当对象集合需要根据不同的标准进行筛选时。
  • 当筛选逻辑可能变化,或者需要动态地组合多个筛选条件时。

7、组合模式

组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。

这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。

7.1、实现方式

  • 统一接口:定义一个接口,所有对象(树枝和叶子)都实现这个接口。
  • 组合结构:树枝对象包含一个接口的引用列表,这些引用可以是叶子或树枝。

案例
我们有一个类 Employee,该类被当作组合模型类。CompositePatternDemo 类使用 Employee 类来添加部门层次结构,并打印所有员工。
在这里插入图片描述
步骤 1
创建 Employee 类,该类带有 Employee 对象的列表。

Employee.java
import java.util.ArrayList;
import java.util.List;
 
public class Employee {
   private String name;
   private String dept;
   private int salary;
   private List<Employee> subordinates;
 
   //构造函数
   public Employee(String name,String dept, int sal) {
      this.name = name;
      this.dept = dept;
      this.salary = sal;
      subordinates = new ArrayList<Employee>();
   }
 
   public void add(Employee e) {
      subordinates.add(e);
   }
 
   public void remove(Employee e) {
      subordinates.remove(e);
   }
 
   public List<Employee> getSubordinates(){
     return subordinates;
   }
 
   public String toString(){
      return ("Employee :[ Name : "+ name 
      +", dept : "+ dept + ", salary :"
      + salary+" ]");
   }   
}

步骤 2
使用 Employee 类来创建和打印员工的层次结构。

CompositePatternDemo.java
public class CompositePatternDemo {
   public static void main(String[] args) {
      Employee CEO = new Employee("John","CEO", 30000);
 
      Employee headSales = new Employee("Robert","Head Sales", 20000);
 
      Employee headMarketing = new Employee("Michel","Head Marketing", 20000);
 
      Employee clerk1 = new Employee("Laura","Marketing", 10000);
      Employee clerk2 = new Employee("Bob","Marketing", 10000);
 
      Employee salesExecutive1 = new Employee("Richard","Sales", 10000);
      Employee salesExecutive2 = new Employee("Rob","Sales", 10000);
 
      CEO.add(headSales);
      CEO.add(headMarketing);
 
      headSales.add(salesExecutive1);
      headSales.add(salesExecutive2);
 
      headMarketing.add(clerk1);
      headMarketing.add(clerk2);
 
      //打印该组织的所有员工
      System.out.println(CEO); 
      for (Employee headEmployee : CEO.getSubordinates()) {
         System.out.println(headEmployee);
         for (Employee employee : headEmployee.getSubordinates()) {
            System.out.println(employee);
         }
      }        
   }
}

步骤 3
执行程序,输出结果为:

Employee :[ Name : John, dept : CEO, salary :30000 ]
Employee :[ Name : Robert, dept : Head Sales, salary :20000 ]
Employee :[ Name : Richard, dept : Sales, salary :10000 ]
Employee :[ Name : Rob, dept : Sales, salary :10000 ]
Employee :[ Name : Michel, dept : Head Marketing, salary :20000 ]
Employee :[ Name : Laura, dept : Marketing, salary :10000 ]
Employee :[ Name : Bob, dept : Marketing, salary :10000 ]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/720578.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux系统之mtr命令的基本使用

Linux系统之mtr命令的基本使用 一、mtr命令介绍二、mtr命令使用帮助2.1 mtr命令的帮助信息2.2 mtr帮助信息解释 三、安装mtr工具四、mtr命令的基本使用4.1 直接使用4.2 设定ping次数4.3 禁用DNS解析4.4 显示IP地址4.5 调整间隔 五、总结 一、mtr命令介绍 mtr命令是一个网络诊断…

Hype4.0 for Mac软件下载-Hype for Mac HTML5 创作工具下载附加详细安装步骤

Hype 4 Pro Mac正式版是款功能实用的动画创作工具。Hype 4 Pro Mac最新版可以帮您轻松创建令人惊叹的动画和交互式网页内容。并且Hype 4 Pro Mac还可被设计师用来创建动画&#xff0c;为网页、信息图形、演示文稿、数字杂志、广告、iBooks、教育内容、应用程序原型、作品集、动…

46. 【Java教程】Optional 类

上一小节&#xff0c;我们接触到了Optional类&#xff0c;但没有详细展开介绍&#xff0c;Optional类也是 Java 8 新加入的类。本小节我们就来学习一下这个类&#xff0c;你将了解到Optional类的解决了什么问题&#xff0c;如何创建Optioanl类的对象&#xff0c;它又有哪些常用…

【MySQL进阶之路 | 高级篇】SQL执行过程

1. 客户端与服务器的连接 运行中的服务器程序与客户端程序本质上都是计算机的一个进程&#xff0c;所以客户端进程向服务器端进程发送请求并得到相应过程的本质就是一个进程间通信的过程. 我们可以使用TCP/IP网络通信协议&#xff0c;命名管道和共享内存等方式&#xff0c;实…

2024/6/18 英语每日一段

While refusing to attribute various problems to specific labs in order to protect the investigators’ sources, the Gladstone AI team told The Washington Times that it found various assessments of security issues were “totally untethered to reality” about…

【STM32】GPIO简介

1.GPIO简介 GPIO是通用输入输出端口的简称&#xff0c;简单来说就是STM32可控制的引脚&#xff0c;STM32芯片的GPIO引脚与外部设备连接起来&#xff0c;从而实现与外部通讯、控制以及数据采集的功能。 STM32芯片的GPIO被分成很多组&#xff0c;每组有16个引脚。 最基本的输出…

RockChip Android12 System之Datetime

一:概述 本文将针对Android12 Settings二级菜单System中Date&time的UI修改进行说明。 二:Date&Time 1、Activity packages/apps/Settings/AndroidManifest.xml <activityandroid:name="Settings$DateTimeSettingsActivity"android:label="@stri…

ubuntu 18.04 安装vnc

如何在Ubuntu 18.04安装VNC | myfreax sudo apt install xfce4 xfce4-goodies xorg dbus-x11 x11-xserver-utils sudo apt install tigervnc-standalone-server tigervnc-common vncserver sudo apt install xfce4 xfce4-goodies xorg dbus-x11 x11-xserver-utils sudo apt ins…

胡说八道(24.6.17)——STM32以及通信杂谈

之前的文章中咱们谈到了STM32的时钟&#xff0c;今天我们来联系实际&#xff0c;来看看内部时钟下和外部时钟下的两种不同时钟的电平翻转。本次终于有硬件了&#xff0c;是最基础的STM32F103C8T6。 首先是&#xff0c;内部时钟的配置操作。 系统的内部时钟是72MHz&#xff0c;由…

2021年9月电子学会青少年软件编程 中小学生Python编程等级考试三级真题解析(选择题)

2021年9月Python编程等级考试三级真题解析 选择题(共25题,每题2分,共50分) 1、使用map函数可以实现列表数据元素类型的转换,而无需通过循环。则将列表L=[1,3,5,7,9]转换为列表[1,3,5,7,9]的函数写法为 A、map(int,L) B、list(map(int,L)) C、map(L,int) D、list(map…

归并排序与计数排序

博主主页: 码农派大星. 数据结构专栏:Java数据结构 数据库专栏:MySQL数据库 JavaEE专栏:JavaEE 关注博主带你了解更多数据结构知识 1.归并排序 1.递归实现归并排序 归并排序 //归并排序public static void mergeSort(int[] array) {mergeSortFun(array,0,array.length-1); r…

重生奇迹MU召唤术师简介

出生地&#xff1a;幻术园 性 别&#xff1a;女 擅 长&#xff1a;召唤幻兽、辅助魔法&攻击魔法 转 职&#xff1a;召唤巫师&#xff08;3转&#xff09; 介 绍&#xff1a;从古代开始流传下来的高贵的血缘&#xff0c;为了种族纯正血缘的延续及特殊使用咒术的天赋&…

2024.6.18

Python的网络编程 网络四层 在开始前,我们需要先了解一下我们在网络通信过程中的四个层次 我们上网产生的数据都是经过协议栈一层一层的封装然后经网卡发送到网络&#xff0c;经网络发送到服务端&#xff0c;然后服务端又是一层一层的解封装拿到自己想要的数据。 我们学习的…

基于B/S版java语言+SpringBoot技术开发的云HIS系统源码 HIS系统住院业务模块常见问题及解决方案

基于B/S版java语言SpringBoot技术开发的云HIS系统源码 HIS系统住院业务模块常见问题及解决方案 随着医疗技术的不断提高&#xff0c;住院治疗已成为许多病人的常规选择。但是&#xff0c;住院治疗不仅需要医护人员的精心照顾&#xff0c;也需要个高效的信息系统来保证整个治疗过…

Maven 配置学习:存在两个本地私服如何配置

Maven 配置学习&#xff1a;存在两个本地私服如何配置 目录 Maven 配置学习&#xff1a;存在两个本地私服如何配置解释&#xff1a;1.本地仓库位置&#xff1a;2.Profiles 定义&#xff1a;3.Repositories 定义顺序&#xff1a;4.Active Profiles&#xff1a; 操作步骤&#xf…

已解决:geecg Column ‘id‘ in order clause is ambiguous

报错&#xff1a;Column id in order clause is ambiguous&#xff1b; MyBatis关联查询&#xff0c;相同字段名冲突&#xff0c;sql语句已经使用别名但仍然报错。 分析&#xff1a;写mapper映射文件时&#xff0c;在写到一对一关联&#xff0c;一对多关联时&#xff0c;由于两…

【神经网络】基于CNN(卷积神经网络)构建猫狗分类模型

文章目录 解决问题数据集探索性数据分析数据预处理数据集分割数据预处理 构建模型并训练构建模型训练模型 结果分析与评估模型保存结果预测经验总结 解决问题 针对经典猫狗数据集&#xff0c;基于卷积神经网络&#xff0c;构建猫狗二元分类模型&#xff0c;使用数据集进行参数…

RoboDK试用期间提示无效或过期的许可证

问题描述 RoboDK下载下来在试用期间提示如下信息&#xff0c;不知道什么原因 临时解决方法 将C:\Users\${username}\AppData\Roaming\RoboDK该目录下的文件全部删除掉&#xff0c;便可以正常使用RoboDK应用了&#xff0c;但是等软件关闭后还是会出现上面的问题&#xff0c;…

IPython 使用技巧整理

IPython 是一个强大的交互式 Python shell&#xff0c;广泛用于数据分析、科学计算和开发工作。本文将整理一些 IPython 的实用技巧&#xff0c;帮助你更高效地使用 IPython。 目录 快速启动和退出魔法命令高效的代码编写变量和对象信息历史命令IPython 扩展错误调试与 Jupy…

js中!emailPattern.test(email) 的test是什么意思

test 是 JavaScript 正则表达式&#xff08;RegExp&#xff09;对象的方法之一&#xff0c;用于测试一个字符串是否与正则表达式匹配。正则表达式是一种用于匹配字符串的模式&#xff0c;通常用于验证输入数据、查找和替换文本等。 使用 test 方法 test 方法语法如下&#xf…