ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# 如何在 Java 中创建不可变的类 > 原文: [https://howtodoinjava.com/java/basics/how-to-make-a-java-class-immutable/](https://howtodoinjava.com/java/basics/how-to-make-a-java-class-immutable/) [不可变类](https://en.wikipedia.org/wiki/Immutable_object)是其状态一旦创建便无法更改的类。 有某些准则可以**创建 Java** 中不可变的类。 在这篇文章中,我们将重新审视这些准则。 ```java Table of Contents 1\. Rules to create immutable classes 2\. Java immutable class example 3\. Benefits of making a class immutable 5\. Summary ``` ## 1\. 创建不可变类的规则 Java 文档本身具有一些准则,[此链接中](https://docs.oracle.com/javase/tutorial/essential/concurrency/imstrat.html)的这些准则可以确定编写不可变类。 通过使用`Date`字段创建具有可变对象的不可变类,我们将理解这些准则的实际含义。 1. #### 不要提供“设置器”方法 - 修改字段或字段引用的对象的方法。 该原则表明,对于您的类中的所有可变属性,请勿提供设置器方法。 设置器方法用于更改对象的状态,这是我们在此要避免的。 2. #### 将所有字段定为最终和私有的 这是增加*不变性*的另一种方法。 声明为`private`的字段将无法在类之外访问,并且将它们设置为最终字段将确保即使您无意中也无法更改它们。 3. #### 不允许子类覆盖方法 最简单的方法是将该类声明为`final`。 Java 中的`final`类无法扩展。 4. #### 具有可变实例变量时要特别注意 始终记住,实例变量将是**可变的**或**不可变的**。 标识它们并返回具有所有可变对象复制内容的新对象。 不可变的变量可以安全地返回而无需额外的努力。 一种更复杂的方法是使构造器`private`,并在**工厂方法**中构造实例。 ## 2\. Java 不可变类示例 让我们将以上所有规则应用于不可变类,并为 Java 中的**不可变类实现具体的类实现**。 ```java import java.util.Date; /** * Always remember that your instance variables will be either mutable or immutable. * Identify them and return new objects with copied content for all mutable objects. * Immutable variables can be returned safely without extra effort. * */ public final class ImmutableClass { /** * Integer class is immutable as it does not provide any setter to change its content * */ private final Integer immutableField1; /** * String class is immutable as it also does not provide setter to change its content * */ private final String immutableField2; /** * Date class is mutable as it provide setters to change various date/time parts * */ private final Date mutableField; //Default private constructor will ensure no unplanned construction of class private ImmutableClass(Integer fld1, String fld2, Date date) { this.immutableField1 = fld1; this.immutableField2 = fld2; this.mutableField = new Date(date.getTime()); } //Factory method to store object creation logic in single place public static ImmutableClass createNewInstance(Integer fld1, String fld2, Date date) { return new ImmutableClass(fld1, fld2, date); } //Provide no setter methods /** * Integer class is immutable so we can return the instance variable as it is * */ public Integer getImmutableField1() { return immutableField1; } /** * String class is also immutable so we can return the instance variable as it is * */ public String getImmutableField2() { return immutableField2; } /** * Date class is mutable so we need a little care here. * We should not return the reference of original instance variable. * Instead a new Date object, with content copied to it, should be returned. * */ public Date getMutableField() { return new Date(mutableField.getTime()); } @Override public String toString() { return immutableField1 +" - "+ immutableField2 +" - "+ mutableField; } } ``` 现在该测试我们的类了: ```java class TestMain { public static void main(String[] args) { ImmutableClass im = ImmutableClass.createNewInstance(100,"test", new Date()); System.out.println(im); tryModification(im.getImmutableField1(),im.getImmutableField2(),im.getMutableField()); System.out.println(im); } private static void tryModification(Integer immutableField1, String immutableField2, Date mutableField) { immutableField1 = 10000; immutableField2 = "test changed"; mutableField.setDate(10); } } ``` 程序输出: ```java 100 - test - Tue Oct 30 21:34:08 IST 2012 100 - test - Tue Oct 30 21:34:08 IST 2012 ``` 可以看出,即使使用其引用更改实例变量也不会更改其值,因此该类是不可变的。 #### JDK 中的不可变类 除了您编写的类,JDK 本身还有很多不可变的类。 给出了这样的 Java 不可变类的列表。 1. `String` 2. 包装器类,例如`Integer`,`Long`,`Double`等。 3. 不可变的集合类,例如`Collections.singletonMap()`等。 4. `java.lang.StackTraceElement` 5. Java 枚举(理想情况下应该如此) 6. `java.util.Locale` 7. `java.util.UUID` ## 3\. 使类不可变的好处 首先让我们确定不可变类的**优势。 在 Java 中,不可变类:** 1. 易于构建,测试和使用 2. 自动是线程安全的,并且没有同步问题 3. 不需要复制构造器 4. 不需要克隆的实现 5. 允许 [`hashCode()`](//howtodoinjava.com/java/related-concepts/working-with-hashcode-and-equals-methods-in-java/ "Working with hashCode and equals methods in java") 使用延迟初始化,并缓存其返回值 6. 用作字段时不需要防御性地复制 7. 作为良好的[映射的键和集合元素](//howtodoinjava.com/java/collections/how-hashmap-works-in-java/ "How hashmap works in java")(在集合中这些对象不得更改状态) 8. 在构造时就建立了其类不变式,因此无需再次检查 9. 始终具有“**失败原子性**”(约书亚·布洛赫(Joshua Bloch)使用的术语):如果不可变对象引发异常,则它永远不会处于不希望或不确定的状态 ## 4\. 总结 在本教程中,我们学习了**使用可变对象**和不可变字段创建不可变的 Java 类。 我们还看到了不可变类在应用程序中带来的好处。 作为最佳设计实践,始终旨在使您的应用程序 Java 类不可变。 这样,您始终可以减少程序中与[并发](https://howtodoinjava.com/java-concurrency-tutorial/)相关的缺陷的担心。 如何编写一个不可变的类? 这也可能是[面试问题](https://howtodoinjava.com/java-interview-questions/)。 学习愉快! 阅读更多: [为什么字符串类在 Java 中是不可变的?](https://howtodoinjava.com/java/string/java-interview-question-why-strings-are-immutable/)