Mutable vs. Immutable Objects in Java – Java 中的可变对象与不可变对象

最后修改: 2024年 2月 11日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.导言

When working with objects in Java, understanding the difference between mutable and immutable objects is crucial. These concepts impact the behavior and design of your Java code.

在使用 Java 对象时,了解可变对象和不可变对象之间的区别至关重要。这些概念会影响 Java 代码的行为和设计。

In this tutorial, let’s explore the definitions, examples, advantages, and considerations of both mutable and immutable objects.

在本教程中,我们将探讨可变对象和不可变对象的定义、示例、优点和注意事项。

2. Immutable Objects

2.不可变对象

Immutable objects are objects whose state cannot be changed once they are created. Once an immutable object is instantiated, its values and properties remain constant throughout its lifetime.

不可变对象是指创建后其状态无法更改的对象。不可变对象实例化后,其值和属性在整个生命周期内保持不变。

Let’s explore some examples of built-in immutable classes in Java.

让我们来探讨 Java 内置不可变类的一些示例。

2.1. String Class

2.1.字符串

The immutability of Strings in Java ensures thread safety, enhances security, and helps with the efficient use of memory through the String Pool mechanism.

Java 中 Strings 的不变性可确保线程安全、增强安全性,并通过 String Pool 机制帮助高效使用内存。

@Test
public void givenImmutableString_whenConcatString_thenNotSameAndCorrectValues() {
    String originalString = "Hello";
    String modifiedString = originalString.concat(" World");

    assertNotSame(originalString, modifiedString);

    assertEquals("Hello", originalString);
    assertEquals("Hello World", modifiedString);
}

In this example, the concat() method creates a new String, and the original String remains unchanged.

在这个示例中,concat() 方法创建了一个新的 字符串,而原来的 String 保持不变。

2.2. Integer Class

2.2.整数

In Java, the Integer class is immutable, meaning its values cannot be changed once they are set. However, when you perform operations on an Integer, a new instance is created to hold the result.

在 Java 中,Integer是不可变的,这意味着其值一旦设置就无法更改。然而,当您对 Integer 执行操作时,将创建一个新实例来保存结果。

@Test
public void givenImmutableInteger_whenAddInteger_thenNotSameAndCorrectValue() {
    Integer immutableInt = 42;
    Integer modifiedInt = immutableInt + 8;

    assertNotSame(immutableInt, modifiedInt);

    assertEquals(42, (int) immutableInt);
    assertEquals(50, (int) modifiedInt);
}

Here, the + operation creates a new Integer object, and the original object remains immutable.

在这里,”+”操作会创建一个新的 Integer 对象,而原始对象则保持不变。

2.3. Advantages of Immutable Objects

2.3.不可变对象的优势

Immutable objects in Java offer several advantages that contribute to code reliability, simplicity, and performance. Let’s understand some of the benefits of using immutable objects:

Java 中的不可变对象具有多种优势,有助于提高代码的可靠性、简洁性和性能。让我们来了解一下使用不可变对象的一些好处:

  • Thread Safety: Immutability inherently ensures thread safety. Since the state of an immutable object cannot be modified after creation, it can be safely shared among multiple threads without the need for explicit synchronization. This simplifies concurrent programming and reduces the risk of race conditions.
  • Predictability and Debugging: The constant state of immutable objects makes code more predictable. Once created, an immutable object’s values remain unchanged, simplifying reasoning about code behavior.
  • Facilitates Caching and Optimization: Immutable objects can be easily cached and reused. Once created, an immutable object’s state does not change, allowing for efficient caching strategies.

Therefore, developers can design more robust, predictable, and efficient systems using immutable objects in their Java applications.

因此,开发人员可以在其 Java 应用程序中使用不可变对象来设计更稳健、更可预测和更高效的系统。

3. Creating Immutable Objects

3.创建不可变对象

To create an immutable object, let’s consider an example of a class named ImmutablePerson. The class is declared as final to prevent extension, and it contains private final fields with no setter methods, adhering to the principles of immutability.

要创建不可变对象,让我们以一个名为 ImmutablePerson 的类为例。该类被声明为final以防止扩展,它包含没有设置方法的private final字段,符合不可变原则。

public final class ImmutablePerson {
    private final String name;
    private final int age;

    public ImmutablePerson(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

Now, let’s consider what happens when we attempt to modify the name of an instance of ImmutablePerson:

现在,让我们来看看当我们试图修改 ImmutablePerson 实例的名称时会发生什么:

ImmutablePerson person = new ImmutablePerson("John", 30);
person.setName("Jane"); 

The attempt to modify the name of an ImmutablePerson instance will result in a compilation error. This is because the class is designed to be immutable, with no setter methods allowing changes to its state after instantiation.

尝试修改 ImmutablePerson 实例的名称将导致编译错误。这是因为该类被设计为不可变的,没有允许在实例化后更改其状态的设置器方法。

The absence of setters and the declaration of the class as final ensure the immutability of the object, providing a clear and robust way to handle a constant state throughout its lifecycle.

不使用设置器和将类声明为final可确保对象的不变性,为在整个生命周期中处理恒定状态提供了一种清晰而稳健的方法。

4. Mutable Objects

4.可变对象

Mutable objects in Java are entities whose state can be modified after their creation. This mutability introduces the concept of changeable internal data, allowing values and properties to be altered during the object’s lifecycle.

Java中的可变对象是其状态可在创建后修改的实体。这种可变性引入了可更改内部数据的概念,允许在对象的生命周期内更改值和属性。

Let’s explore a couple of examples to understand their characteristics.

让我们通过几个例子来了解它们的特点。

4.1. StringBuilder Class

4.1.字符串生成器

The StringBuilder class in Java represents a mutable sequence of characters. Unlike its immutable counterpart, String, a StringBuilder allows the dynamic modification of its content.

Java 中的 StringBuilder表示可变的字符序列。与不可变的对应类 String 不同,StringBuilder 允许动态修改其内容。

@Test
public void givenMutableString_whenAppendElement_thenCorrectValue() {
    StringBuilder mutableString = new StringBuilder("Hello");
    mutableString.append(" World");

    assertEquals("Hello World", mutableString.toString());
}

Here, the append method directly alters the internal state of the StringBuilder object, showcasing its mutability.

在这里,append 方法直接更改了 StringBuilder 对象的内部状态,展示了其可变性。

4.2. ArrayList Class

4.2.数组列表

The ArrayList class is another example of a mutable object. It represents a dynamic array that can grow or shrink in size, allowing the addition and removal of elements.

ArrayList是另一个可变对象的例子。它表示一个动态数组,其大小可以增大或缩小,允许添加和删除元素。

@Test
public void givenMutableList_whenAddElement_thenCorrectSize() {
    List<String> mutableList = new ArrayList<>();
    mutableList.add("Java");

    assertEquals(1, mutableList.size());
}

The add method modifies the state of the ArrayList by adding an element, exemplifying its mutable nature.

add 方法通过添加一个元素来修改 ArrayList 的状态,体现了其可变性。

4.3. Considerations

4.3.考虑因素

While mutable objects offer flexibility, they come with certain considerations that developers need to be mindful of:

虽然可变对象具有灵活性,但它们也有一些开发人员需要注意的问题:

  • Thread Safety: Mutable objects may require additional synchronization mechanisms to ensure thread safety in a multi-threaded environment. Without proper synchronization, concurrent modifications can lead to unexpected behavior.
  • Complexity in Code Understanding: The ability to modify the internal state of mutable objects introduces complexity in code understanding. Developers need to be cautious about the potential changes to an object’s state, especially in large codebases.
  • State Management Challenges: Managing the internal state of mutable objects requires careful consideration. Developers should track and control changes to ensure the object’s integrity and prevent unintended modifications.

Despite these considerations, mutable objects provide a dynamic and flexible approach, allowing developers to adapt the state of an object based on changing requirements.

尽管有这些考虑因素,可变对象提供了一种动态和灵活的方法,允许开发人员根据不断变化的需求调整对象的状态。

5. Mutable vs. Immutable Objects

5.可变对象与不可变对象

When contrasting mutable and immutable objects, several factors come into play. Let’s explore the fundamental differences between these two types of objects:

在对比可变对象和不可变对象时,有几个因素会发挥作用。让我们来探讨一下这两类对象的根本区别:

Criteria Mutable Objects Immutable Objects
Modifiability Can be changed after creation Remain constant once created
Thread Safety May require synchronization for thread safety Inherently thread-safe
Predictability May introduce complexity in understanding Simplifies reasoning and debugging
Performance Impact Can impact performance due to synchronization Generally has a positive impact on performance

5.1. Choosing Between Mutability and Immutability

5.1.在可变性和不变性之间做出选择

The choice between mutability and immutability relies on the application’s requirements. If adaptability and frequent changes are necessary, opt for mutable objects. However, if consistency, safety, and a stable state are priorities, immutability is the way to go.

在可变性和不可变性之间做出选择取决于应用程序的要求。如果需要适应性和频繁更改,则应选择可变对象。但是,如果一致性、安全性和稳定的状态是优先考虑的因素,那么不可变性就是首选。

Consider the concurrency aspect in multitasking scenarios. Immutability simplifies data sharing among tasks without the complexities of synchronization.

考虑多任务场景中的并发性。互操作性简化了任务间的数据共享,而无需复杂的同步。

Additionally, assess your application’s performance needs. While immutable objects generally enhance performance, weigh whether this boost is more significant than the flexibility offered by mutable objects, especially in situations with infrequent data changes.

此外,还要评估应用程序的性能需求。虽然不可变对象通常会提高性能,但要权衡这种提高是否比可变对象提供的灵活性更重要,尤其是在数据变化不频繁的情况下。

Maintaining the right balance ensures your code aligns effectively with your application’s demands.

保持适当的平衡可确保您的代码有效地满足应用程序的需求。

6. Conclusion

6.结论

In conclusion, the choice between mutable and immutable objects in Java plays a crucial role in shaping the reliability, efficiency, and maintainability of your code. While immutability provides thread safety, predictability, and other advantages, mutability offers flexibility and dynamic state changes.

总之,Java 中可变对象和不可变对象之间的选择对代码的可靠性、效率和可维护性起着至关重要的作用。不可变性提供了线程安全性、可预测性和其他优势,而可变性则提供了灵活性和动态状态更改。

Assessing your application’s requirements and considering factors such as concurrency, performance, and code complexity will help in making the appropriate choice for designing resilient and efficient Java applications.

评估应用程序的要求,并考虑并发性、性能和代码复杂性等因素,将有助于做出适当的选择,以设计弹性和高效的 Java 应用程序。

You can find the examples used in this article over on GitHub.

您可以在 GitHub 上找到本文使用的示例