在 Java 中创建不可变的自定义类2024年9月10日 | 阅读 9 分钟 Java 中的不变性(Immutability)是指创建对象后,其状态无法被修改的概念。不变性在并发编程中尤其有用,因为它可以消除同步的需要并提供一定的线程保护。一种实现持续改进的方法是创建遵循一组指导方针的自定义类,以确保其模式在生命周期内保持一致。 不可变对象的特征Java 中的不可变对象具有以下特征: 状态不可修改: 一旦创建了不可变对象,其状态就无法更改。所有字段都是 final 的,并且没有 setter 方法。 无修改器方法: 不可变对象没有修改其状态的方法。任何看似修改对象的操作都会返回一个带有更新值的新实例。 字段为 final: 不可变类中的所有字段都声明为 final,以确保在对象创建后无法重新赋值。 不允许通过子类修改: 为防止通过子类化进行潜在的修改,该类通常被标记为 final,或者其构造函数通过静态工厂方法设为私有。 创建不可变自定义类让我们以一个假设的 Person 类为例,通过以下步骤创建 Java 中的不可变自定义类。 步骤 1:声明类为 Final 为防止子类化,将类声明为 final。 步骤 2:声明字段为 Final 确保所有字段都是 final。 步骤 3:提供构造函数 在构造函数中初始化所有字段,并避免直接暴露可变对象。 步骤 4:无 Setter 方法 避免提供 setter 方法或任何修改状态的方法。 步骤 5:防御性拷贝 如果类包含可变对象,请确保它们不能直接访问,并在需要时提供防御性拷贝。 步骤 6:返回克隆或不可变对象 从方法返回可变对象时,返回克隆或不可变实例以保持不变性。 步骤 7:重写 equals() 和 hashCode() 重写 equals() 和 hashCode() 方法,以确保在使用集合中的实例时行为正确。 步骤 8:枚举 (Enum) 的不变性 Java 中的枚举 (Enum) 是隐式 final 的,并且具有固定的实例,这使得它们非常适合不变性。如果我们有一组常量值,请考虑使用枚举。 步骤 9:懒惰初始化 如果对象的创建成本很高,并且其值不一定始终需要,我们可以采用懒惰初始化。仅在请求时计算并缓存值。 步骤 10:有效使用 final 关键字 除了将字段标记为 final 外,还可以考虑在适用时使用 final 关键字修饰方法和类。将方法标记为 final 可以防止子类重写它们,从而为您的设计增加额外的保护层。 步骤 11:不变性与集合 在处理集合时,请确保集合中的元素也是不可变的,或者提供防御性拷贝以保持类的不变性。Java 提供了 Collections.unmodifiableList() 和 Collections.unmodifiableMap() 等实用类来创建集合的不可修改视图。 步骤 12:序列化 为了使不可变类能够正确序列化,请确保所有字段都可序列化。实现 Serializable 接口,并确保字段在引用不可序列化对象时被标记为 transient。 步骤 13:有效使用 Objects 类 Java 的 Objects 类提供了处理 null 值以及处理 equals 和 hash code 操作的实用方法。使用这些方法可以简化您的代码并使其更简洁。 步骤 14:命名约定的一致性 对不可变类、方法和字段遵循一致的命名约定。这有助于提高代码的可读性和可维护性。 现在,让我们创建一个不可变的 Person 类的完整示例,以及一个演示其用法的简单程序。以下是代码: 文件名:Person.java 现在,让我们创建一个简单的 Main 类来演示如何使用 Person 类。 文件名:CustomImmutable.java 输出 Person 1: Person{name='Alice', age=30} Person 2: Person{name='Bob', age=25} Are person1 and person2 equal? false HashCode of person1: -1061542078 HashCode of person2: -595926066 正如我们所见,我们成功地创建了 Person 类的实例,演示了创建后其状态无法被修改,并展示了如何实现 equality 和 hashCode() 方法。输出也证实了这些实例是不可变的,因为尝试修改它们的状态会导致编译错误。 要保存和运行 Java 程序,请遵循以下步骤: 保存程序 打开文本编辑器: 您可以使用任何您喜欢的文本编辑器,例如记事本(Windows)、TextEdit(macOS)或任何代码编辑器,如 Visual Studio Code、IntelliJ IDEA 或 Eclipse。 复制代码: 复制上面提供的 Person.java 和 CustomImmutable.java 代码,并将它们粘贴到文本编辑器中的单独文件中。将 Person.java 文件保存为 Person.java,将 CustomImmutable.java 文件保存为 CustomImmutable.java。 选择一个目录: 将这两个文件保存在计算机上您选择的任何目录(文件夹)中。为您的 Java 项目创建一个专用文件夹是一个好习惯。 运行程序 打开终端或命令提示符: 导航到您保存 Java 文件的目录。您可以使用终端或命令提示符中的 cd 命令来完成此操作。 编译 Java 文件: 使用 javac 命令编译 Person.java 和 CustomImmutable.java 文件。它将生成相应的 .class 文件。 运行程序: 成功编译后,使用 java 命令执行 CustomImmutable 类来运行程序。 查看输出: 程序将执行,我们将在终端或命令提示符中看到打印的输出。 让我们深入探讨 Java 中不可变类的某些高级概念和最佳实践。 1. 有效使用 LocalDateTime 和 ImmutableList在处理 Java 中的日期和时间时,请考虑使用 java.time 包中的 LocalDateTime,它是不可变的且线程安全的。同样,在处理集合时,请使用 Guava 库中的 ImmutableList 来确保不变性。 2. 使用 java.util.concurrent 实现线程安全对于并发应用程序,请使用 java.util.concurrent 包中的类来确保线程安全。例如,AtomicInteger 和 AtomicReference 分别为整数值和对象引用提供原子操作。 3. 使用 Builder 模式创建不可变对象利用 Builder 模式来构建具有许多属性的复杂不可变对象。此模式允许灵活地创建对象,同时确保不变性。 4. 不变性与性能不变性有时可以通过减少同步的需要并允许 JVM 进行更好的优化来提高性能。但是,在处理大型对象或频繁的状态更改时要小心,因为创建新实例可能会导致内存使用量增加和垃圾回收开销。 5. 防御性拷贝与外部可变性当从不可变类返回可变对象时,请确保进行防御性拷贝,以防止外部修改。这适用于集合、数组和其他可变对象。 结论在 Java 中创建不可变自定义类需要仔细选择策略,以确保类实例在创建后无法更改。遵循上述指南可以产生健壮且简单的规则,这在并发编程中很重要。采用不变性可以让开发人员编写易于理解、维护和评估的代码。 |
在 Java 中,当组织包含重复元素的集合以及借助 Multiset 统计元素频率时。Java SE 在其标准库中不支持 Multiset 作为接口,但它可以由第三方框架(如 Google...)支持。
5 分钟阅读
Java 是一种通用且功能强大的编程语言,以其强大的类型系统而闻名。增强类型安全并促进代码重用的关键功能之一是边界类型。边界类型允许开发人员对可用作泛型的类型施加约束...
5 分钟阅读
Sexagesimal(六十进制)是一种测量系统,其中角度以度、分和秒为单位进行测量。在 Java 中,SexagesimalFormatter 是 herschel.share.fltdyn.math 包中的一个格式化类。它扩展了 Object 类。它用于解析和格式化 sexagesimal 值。基数为 60 的值是...
阅读 2 分钟
按位补码运算符属于一元运算符(只处理一个操作数)的类别。它接收一个数字并反转其所有位。当对位应用按位运算符时,1会变成0,0会变成1...
阅读 3 分钟
在 Java 编程世界中,处理 HTTP 请求和响应对于应用程序开发至关重要。HttpEntity 类是处理 HTTP 请求和响应时的关键组件,它允许我们读写 HTTP 连接中的数据。在本节中,我们将...
阅读 4 分钟
在 Java 中,Variant 是一个类,它定义在两个不同的包中。javax.ws.rs.core.Variant org.eclipse.swt.ole.win32.Variant javax.ws.rs.core.Variant 它内置于 JDK 中。该类属于 javax.ws.rs.core.Variant。它扩展了 Object 类。该类用于表示资源表示的变体,其中包含有关媒体类型、语言...的信息。
阅读 3 分钟
面向对象的编程语言 Java 具有许多有助于开发人员构建适应性强、可重用且可扩展的应用程序的功能。泛型是一种强大的工具,它使程序员能够构建与多种数据类型一起工作的类、方法和接口,是 Java 的基本组成部分之一...
阅读 4 分钟
图案因其美学吸引力以及它们为我们的可见世界带来的秩序感而一直吸引着人类。尤其是方形图案,它们简单而优雅,并且可以在 Java 中相对轻松地创建。在本节中,我们将深入...
阅读 4 分钟
? 将米转换为公里是各种 Java 应用程序中的常见任务,尤其是在处理不同尺度的距离或测量值时。幸运的是,执行此转换非常简单,只需要几行代码。在本节中,我们将介绍转换过程...
阅读 3 分钟
在设计表单时,电子邮件起着重要作用。电子邮件可以是我们的用户名或登录 ID。电子邮件有其自身的结构,在使用之前,我们需要对其进行验证。在 Java 中,电子邮件验证是通过使用正则表达式来执行的。电子邮件验证是...
阅读 3 分钟
我们请求您订阅我们的新闻通讯以获取最新更新。
我们提供所有技术(如 Java 教程、Android、Java 框架)的教程和面试问题
G-13, 2nd Floor, Sec-3, Noida, UP, 201301, India