面向对象中不可变性

软件设计中的不可变性是一个非常重要的概念,它可以在多个方面提高代码的可靠性、可维护性和安全性。

从开发者角度(代码提供者):

在软件开发过程中,当某个对象的属性是不可变的时候,这意味着这些属性的值在对象创建后不会改变。这种不可变性对于开发者来说有着重要的意义:

  • 代码逻辑假设的保护:当开发者在编写代码时,可以假设某个属性是不可变的,从而建立起相应的代码逻辑。如果这个假设被打破,可能会导致代码错误或者逻辑混乱。因此,对象属性的不可变性可以保护代码逻辑的正确性,减少代码维护的复杂性。

从使用者角度(代码调用者):

从使用者的角度来看,依赖于对象的某个属性是不可变的意味着使用者可以放心地使用这个属性,而不用担心它会在运行时被修改。这样可以降低使用者犯错的可能性:

  • 避免意外修改的风险:使用者不必担心对象的某个属性会在他们不知情的情况下被修改,从而避免了因为意外修改而引发的错误。这种信任关系可以帮助使用者更加自信地使用对象,并且降低了代码出错的风险。

不可变对象Immutable Objects 带来那些好处

可以引用传递,可以缓存

在 Java 中,除了基本类型(小写 byte/short/int/long/float/double/char/boolean)之外,所有其他类型都是引用传递,你也操作这个对象我也操作这个对象引用 这个对象变掉了系统就会出错
不可变性的特性之一是它们可以进行引用传递并且可以缓存。以下是关于这两个方面的总结:

  1. 可引用传递

    • 不可变对象可以被安全地引用传递给其他对象或方法,因为它们的状态不会改变。
    • 当传递不可变对象时,不需要担心对象在传递过程中被修改导致意外的行为。
  2. 缓存

    • 由于不可变对象的值在创建后不会改变,因此它们可以被安全地缓存。
    • 缓存不可变对象可以提高性能,因为它们的值在整个应用程序生命周期内都是固定的,不需要重新计算或重新获取。

线程安全

当谈到线程安全时,不可变对象(Immutable Objects)是一个非常重要的概念。不可变对象的不可变性使它们天然地具有线程安全性,这对于多线程编程非常有价值。以下是对不可变性如何加强线程安全理解的总结:

  1. 不可变对象不可变性保证

    • 不可变对象的值在创建后不可改变,这意味着它们不会出现竞态条件或数据竞争。
    • 因为不可变对象的状态不会改变,所以多个线程可以同时访问它们而不会导致线程安全问题。
  2. 无需同步措施

    • 由于不可变对象不会改变,因此不需要同步措施(如锁)来保护它们的状态。
    • 这消除了因为同步带来的性能开销和可能的死锁、饥饿等问题,简化了多线程编程。
  3. 并发性能提升

    • 不可变对象的线程安全性意味着多个线程可以同时访问它们,而无需等待或竞争锁资源。
    • 这可以提高应用程序的并发性能,尤其是在高并发环境下。
  4. 防止意外修改和副作用

    • 不可变对象的不可变性可以防止意外的状态变化和副作用。
    • 这使得代码更加可靠和易于理解,因为开发者不需要担心对象的状态在其它地方被修改。
  5. 线程安全的集合

    • 不可变集合类(如 ImmutableListImmutableMap 等)提供了线程安全的集合实现。
    • 这些集合类的不可变性确保了多线程环境下的安全访问,避免了对普通集合类进行手动同步的麻烦。
  6. 使用不可变对象的最佳实践

    • 在设计和实现中,尽量使用不可变对象来减少线程安全问题的发生。
    • 在多线程环境中,特别是在高并发场景下,使用不可变对象可以降低线程安全问题的概率,并简化代码的编写和维护。

综上所述,不可变对象的不可变性是实现线程安全的重要手段之一。通过使用不可变对象,可以提高应用程序的并发性能,减少线程安全问题的发生,并简化多线程编程的复杂性。

java中实现不可变性实践

在这里插入图片描述

final 关键字无法保证不可变性

在 Java 中,final 关键字可以用来修饰变量、方法和类,但它并不能完全保证不可变性。尽管使用 final 关键字可以确保变量引用不会改变,但如果引用的对象本身是可变的,那么对象的状态仍然可以被修改。因此,使用 final 关键字只能确保引用的不可变性,而不是对象本身的不可变性。

从接口定义、类的实现上保证不可变性

要实现真正的不可变性,需要从接口定义和类的实现两个方面来保证。首先,在接口定义中应该尽可能地限制对于对象状态的访问和修改,只提供读取数据的方法而不提供修改数据的方法。其次,在类的实现中需要确保所有的属性都是私有的,并且不提供任何修改属性的方法。通过这种方式,可以确保对象的状态在创建后不会改变,从而实现了真正的不可变性。

Collections.unmodifiableXXX 方法(确保List引用的不可变性方法)

Java 中的 Collections 类提供了一系列静态方法来创建不可变的集合对象,如 unmodifiableList()unmodifiableSet()unmodifiableMap() 等。这些方法接受一个普通的集合对象,并返回一个不可变的视图。这样一来,即使原始集合对象发生改变,返回的不可变视图也不会受到影响。

// 定义 Employee 类作为基类
public class Employee {
    private final String name;
    private final int salary;

    // 构造方法
    public Employee(String name, int salary) {
        this.name = name;
        this.salary = salary;
    }

    // 获取员工姓名
    public String getName() {
        return name;
    }

    // 获取员工薪水
    public int getSalary() {
        return salary;
    }
}
// 定义 Manager 类作为 Employee 的子类
public class Manager extends Employee {
    // 使用 final 关键字确保 reporters 属性不可变
    private final List<Employee> reporters;

    // 构造方法
    public Manager(String name, int salary, List<Employee> reporters) {
        // 调用父类的构造方法
        super(name, salary);
        // 创建一个新的 ArrayList 对象,并将 reporters 中的元素复制到新列表中
        List<Employee> tmpList = new ArrayList<>(reporters);
        // 使用 Collections.unmodifiableList() 方法确保不可变性
        this.reporters = Collections.unmodifiableList(tmpList);
    }

    // 获取下属列表
    public List<Employee> getReporters() {
        return reporters;
    }
}

在这里插入图片描述

在上述代码中,为了确保 reporters 属性的不可变性,使用了 new ArrayList<>(reporters) 的方式来创建一个新的 ArrayList 对象。这种做法是为了防止外部的修改影响到 reporters 属性。

具体来说,new ArrayList<>(reporters) 会创建一个新的 ArrayList 对象,并将 reporters 中的元素复制到这个新的列表中。这样一来,即使外部对原始的 reporters 列表进行了修改,新创建的 ArrayList 对象不受影响,保持了原始状态。然后,通过 Collections.unmodifiableList() 方法对这个新的列表进行包装,以确保它的不可变性。

这样做的目的是为了避免外部对 reporters 列表的直接修改,从而保护了对象的不可变性。这种做法是不可变对象的一种常见实现方式,能够确保对象在创建后不会被修改,从而提高了代码的可靠性和安全性。

例如,Collections.unmodifiableList() 方法可以将一个普通的 List 对象转换为不可变的列表,从而确保列表的内容不会被修改。

综上所述,通过合理地设计接口和类,并结合使用 Collections.unmodifiableXXX() 方法,可以在 Java 中实现不可变性,并确保对象在创建后不会被修改,从而提高程序的健壮性和可靠性。

软件设计不可变性的重要性:

综上所述,不可变性在软件设计中具有重要的地位和价值。它可以保护代码逻辑的正确性,降低代码维护的难度;同时也可以增强使用者对于对象的信任,降低使用者犯错的可能性。因此,在软件设计中,应该充分考虑并合理利用不可变性,从而提高软件系统的稳定性、安全性和可维护性。

这样的文章结构可以更好地向读者阐述不可变性的重要性和作用,并且提醒他们在软件设计和开发中要重视不可变性的应用。

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

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

相关文章

用java实现A*寻路算法

前言&#xff1a; 最近的开发中遇到了寻路这个知识点&#xff0c;然后去了解了一下最常见的A算法&#xff0c;本会会结合我的理解&#xff0c;用最通俗易懂的话语讲解A算法的原理&#xff0c;下面会给出代码示例。 说到寻路算法&#xff0c;就涉及到了图的遍历&#xff0c;然…

开年采购云服务器,怎么买最划算?看这篇!

在2024年开年之际&#xff0c;对于许多企业和个人而言&#xff0c;采购云服务器已成为一项重要的决策。云服务器以其灵活性、可扩展性和高可用性等特点&#xff0c;吸引了越来越多的用户。然而&#xff0c;市场上的云服务器提供商众多&#xff0c;如何选择一家值得入手的服务商…

Domain Driven Design (DDD)

Domain Driven Design (DDD领域驱动设计)主要是业务分类例如&#xff08;订单、合同、生产、检测、物流、运输等&#xff09;&#xff0c;独立单元相互不干扰&#xff0c;仅暴露接口的模型。核心在Domain&#xff0c;所有业务模块放这边&#xff0c;当然我们做的时候微服务是一…

如何对接1688平台官方开发平台的商品发布/商品过期处理/商品订单接口?

custom-自定义API操作 API测试 注册开通 1688.custom 1688平台官方开放接口 公共参数 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;secretString是调用密钥api_nameString是API接口名称&#xff08;包括在请求地址中&#xff09;[…

windows部署ruoyi-vue-pro

前提 安装java 安装maven 安装redis mysql 源代码下载 后端 ruoyi-vue-pro 前端 yudao-ui-admin-vue3 后端项目 配置maven 导入数据 CREATE DATABASE ruoyi_vue_pro;修改mysql连接配置 修改redis 打包项目 mvn clean install package -Dmaven.test.skiptrue启动YudaoSe…

TC397 Tasking CMake Gitlab CI CD 环境配置

文章目录 Aurix Development Studio 新建工程与配置Tasking 环境配置CMake 集成Win CMake MinGW 安装Tasking Toolchain 工具链CMakeLists.txtPowershell 脚本 Gitlab CI CDGithub Link 本篇先演示了ADS新建激活编译工程, 讲述了浮点模型, 链接脚本文件, 静态库集成等的设置, 接…

python并发编程:IO模型

一 IO模型 二 network IO 再说一下IO发生时涉及的对象和步骤。对于一个network IO \(这里我们以read举例\)&#xff0c;它会涉及到两个系统对象&#xff0c;一个是调用这个IO的process \(or thread\)&#xff0c;另一个就是系统内核\(kernel\)。当一个read操作发生时&#xff…

C语言学习--摩尔投票算法

目录 1.引入 2.摩尔投票算法 3.具体步骤 3.1抵消阶段 3.2检验过程 4.代码实现 5.总结 1.引入 今天做题看到一个解题思路真的看不懂&#xff0c;一艘才知道是这个算法。 int majorityElement(int* nums, int numsSize) { int notenums[0]; int count1; for(int i1;i<n…

day7-网络编程

1>基于UDP的网络聊天室 Ser.c #include <myhead.h> #define SER_IP "10.211.55.9" // 服务器IP #define SER_PORT 9999struct user {char usrName[20];struct sockaddr_in cin; }; int main(int argc, char const *argv[]) {// 1.创建用于监听的套接字int…

coqui-ai/TTS 案例model文件

GitHub - coqui-ai/TTS: &#x1f438;&#x1f4ac; - a deep learning toolkit for Text-to-Speech, battle-tested in research and production Coqui AI的TTS是一款开源深度学习文本转语音工具&#xff0c;以高质量、多语言合成著称。它提供超过1100种语言的预训练模型库&…

Elemenu中el-table中使用el-popover选中关闭无效解决办法

主要是技术太菜,没找到原因,一点点才找到这个办法解决 因为在el-table-column里,因为是多行,使用trigger"manual" 时,用v-model"visible"来控制时,控件找不到这个值,才换成trigger"click" 先找到弹出关闭事件,再找元素的属性 右键>审核元素…

哪款洗地机值得买?希亦、追觅、米博、美的谁才是行业标杆?

在家庭清洁中&#xff0c;最让我们苦恼的便是厨房垃圾了&#xff0c;油渍跟食物残渣&#xff0c;用扫把扫了后&#xff0c;要反反复复的湿拖五六次&#xff0c;期间不停的手洗拖把&#xff0c;这套流程下来&#xff0c;往往容易腰酸背痛&#xff0c;手指皱巴巴的&#xff0c;这…

Java项目:40 springboot月度员工绩效考核管理系统009

作者主页&#xff1a;源码空间codegym 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 本系统的功能分为管理员和员工两个角色 管理员的功能有&#xff1a; &#xff08;1&#xff09;个人中心管理功能&#xff0c;添加管理员账号…

【BUG修复日志】Anaconda + VSCode 编码错误

【BUG修复日志】Anaconda VSCode 编码错误 平台: Windows11家庭版 (v22621.3155) 软件: Visual Studio Code (v1.87.0) 插件: Python (v2024.2.1) 版本: Conda (v24.1.2)问题描述 VSCode 在安装 Python 插件的情况下自动提示配置 Conda 环境&#xff0c;但是在自动配置完成后…

SpringMVC实用技术

1.校验框架 1.表单校验框架入门 表单校验的重要性 数据可以随意输入&#xff0c;导致错误的结果。表单校验保障了数据有效性、安全性 表单校验分类 校验位置&#xff1a; 客户端校验 服务端校验 校验内容与对应方式&#xff1a; 格式校验 客户端&#xff1a;使用Js技术…

【linuxC语言】系统调用IO文件操作

文章目录 前言一、文件描述符介绍二、系统调用IO API介绍2.1 open函数2.2 close函数2.3 read函数2.4 write函数2.5 lseek函数 三、示例代码总结 前言 在Linux系统中&#xff0c;C语言通过系统调用实现对文件的输入输出&#xff08;I/O&#xff09;操作。系统调用提供了访问操作…

LLM - 使用 Langchain 实现本地 Naive RAG

目录 一.引言 二.构建本地 Langchain 库 1.Doc 知识文档 2.Split 文档切分 3.Encode 内容编码 4.Similar 本地库构建 三.缓存本地 Langchain 库 四.读取本地 Langchain 库 1.Load 读取缓存 2.Similar 预测 3.Add 添加文档 五.总结 一.引言 上一篇博客介绍了当下 R…

M2TS转MP4怎么转?超快的方法~

M2TS格式的优点主要体现在对高清视频的完美支持&#xff0c;能够提供极致的视觉体验。然而&#xff0c;由于其相对较大的文件大小&#xff0c;有时可能不太适合网络传输。此外&#xff0c;部分不支持M2TS的播放设备可能导致一定的兼容性问题。 想要播放m2ts视频&#xff0c;可…

MySQL实战45讲——30答疑文章(二):用动态的观点看加锁

目录 不等号条件里的等值查询 等值查询的过程 怎么看死锁&#xff1f; 怎么看锁等待&#xff1f; update 的例子 小结 上期问题时间 提示 文章摘自林晓斌老师《MySQL实战45讲》&#xff0c;作为笔记而用&#xff0c;故有加一些自己的理解。在第[20]和[21]篇文章中&…

Python基础二

一、变量 在编程中&#xff0c;变量是用来存储数据值的名称。在 Python 中&#xff0c;变量是动态类型的&#xff0c;这意味着你可以将任何类型的数据分配给一个变量&#xff0c;而不需要提前声明变量的类型。 1、全局变量 在函数外部定义的变量是全局变量&#xff0c;可以在程…