Java中的可变对象(Mutable)与不可变对象(Immutable)

2023-11-08 08:25:19 浏览数 (4)

如何在 Java 中创建不可变对象?我以前以为所有对象都是不可变的,因为如果你改变一个 String 实例的内容,它总是会创建一个新的 String 对象并指向该对象。但后来我发现,String 是一个特殊的类,它被特别设计为Immutable,因为它经常被cache。显然,你不能缓存任何不恒定的东西,这就是为什么 String 在 Java 中是不可变的原因。但这鼓励我学习更多有关 Java 中Immutable和Mutable类的知识,以及如何在 Java 中创建自定义的Immutable。

在本文中,我不仅将分享在 Java 中Immutable的步骤,还将讨论可变对象与不可变对象及其优缺点。这也是一个常见的 String 面试问题 ,Java 开发人员也应该意识到这一点。  

Java 中的可变类和不可变类是什么?

在 Java 中,可变类和不可变类的概念指的是对象创建后其状态是否可以更改。可变类是指实例创建后可以修改的类,而不可变类一旦创建就不能改变其状态。

可变对象的状态可以通过修改其字段或属性的方法来改变。例如,StringBuilder 和 ArrayList 都是可变类。例如,你可以add、delete或modify StringBuilder 或 ArrayList 中的元素。

代码语言:javascript复制
StringBuilder mutableString = new StringBuilder("Hello");

mutableString.append(", World!"); // Mutable operation

System.out.println(mutableString.toString()); // Outputs: Hello, World!

不可变类是指实例创建后不可修改的类。不可变对象的状态在创建过程中就已设定,创建后无法更改。例如,String 和 Integer 就是不可变类的例子。一旦创建了 String 对象,就不能更改其中包含的字符。

代码语言:javascript复制
String immutableString = "Hello";


// Immutable operation - returns a new String,
// but doesn't modify the original

immutableString.concat(", World!"); 

System.out.println(immutableString); // Outputs: Hello

因此,你现在应该知道,不可变对象就是其内容不可以更改的对象。所有字段都是final字段的类,或者所有字段都是private字段且没有构造器的类就是几个例子。由于这些字段都是final字段或private字段,因此永远无法从外部更改。这使得它们不可变。

另一方面,可变类允许更改其内容。例如,带有非final字段或带有构造器的private字段的类。由于外部代码可以更改类的内容,因此该类是可变的。

不可变类(如 String)也可以被缓存,在 Java 中,String 被缓存在一个特殊的 String 池中,这主要是为了节省内存,并允许重复使用 String 字面量:

如何在 Java 中创建不可变类?

既然我们已经知道什么是 Java 中的可变类和不可变类,那么现在就来了解一下如何编写不可变类,以及编写不可变类与创建可变类有什么不同。唯一的区别在于如何编写。

下面是一个不可变类的示例:

代码语言:javascript复制
public class CustomImmutableClass {

    public final String customString = ""; //field is final, so it cannot be changed

    private int customInt = 0; //field is private and has no setter, so it cannot be changed

    public int getCustomInt() {

        return customInt;  //CustomInt can be retrieved, but not set.

    }
}

这是可变类的示例:

代码语言:javascript复制
public class CustomMutableClass {
    public String customString = ""; //field is NOT final, so it CAN be changed

    private int customInt = 0; //field is private and has a setter, so it CAN be changed

    public int getCustomInt() {

        return customInt;  //CustomInt can be retrieved
    }

    public void setCustomInt(int customInt) {

        this.customInt = customInt; //customInt can be set
    }

}

不可变类应被标记为 final 类,这样它们就不能被扩展,但仅仅使类成为 final 类并不能使其成为不可变类,尤其是当它可能泄漏状态(如返回一个非 final 的对象和状态的一部分)时。此外,拥有公共的 final 字段也是一种不好的形式。

字符串是不可变的,而大多数对象不是。无论何时使用突变器方法(setSomething 或 addSomething)

返回 void 的对象很可能是可变的。一个突出的例子就是 ArrayList。

要使对象不可变,请确保它们只有非数组的final字段(在 Java 中数组总是可变的),并且所有字段类型也只有final字段。如果不允许访问/更改字段,就可以使用非final字段,但这并不容易推理(但大多数情况下比较容易)。

Java 中不可变对象与可变对象的区别

以下是 Java 中可变类和不可变类之间的一些主要区别:

1. 修改

可变对象在创建后可以修改,但不可变对象在创建后不能修改。

2. 线程安全 可变对象不是线程安全的,如果在多线程环境中使用,可能需要同步以避免数据损坏。另一方面,不可变对象通常是线程安全的,因为状态不能更改并且可以在多个线程之间安全共享。 

3. 状态更改 可变对象允许更改状态,但不可变对象的状态在创建时是固定的。

4. 使用案例 当你需要经常修改对象,或想表示状态会随时间变化的实体时,可变对象就派上用场了。而不可变对象则适用于需要确保对象状态保持不变或需要线程安全的情况。

5. 性能 由于直接修改状态,可变对象在某些场景下可以具有更好的性能,但不可变对象可能涉及创建新对象,可能会影响性能,但在安全性和简单性方面具有优势。

总结

这就是Java 中的不可变类和可变类的全部内容。 本文不仅介绍了什么是可变类和不可变类,还介绍了它们之间的区别。在可变类和不可变类之间做出选择,取决于程序的具体要求和所需对象的特性。不可变类通常是并发或多线程环境中的首选,可以简化对对象状态的推理。

1 人点赞