想更多地了解Java中的不可变对象?


如果对象的状态在构造后无法更改,则认为该对象是不可变的。由于它们无法更改状态,因此它们不会被线程干扰破坏或在不一致的状态下观察到,从而使它们在并发应用程序中很有用。

例如,Java中的String对象是不可变的。

import org.junit.Test;

import static org.junit.Assert.assertEquals;

public class ImmutableTest {
    @Test
    public void testImmutableObjs() {
        String firstName = "yogen";
        String lastName = "rai";

        // concatenate two strings
        String fullName = firstName.concat(" ").concat(lastName);

        assertEquals("yogen", firstName);
        assertEquals("rai", lastName);
        assertEquals("yogen rai", fullName);
    }
}

“”(空白)上的firstName和lastName的串联将不会修改它们中的任何一个,而是创建一个将由fullName引用的新对象。

定义自定义不可变对象 以下规则定义了创建不可变对象的简单策略:

  1. 不要提供“ setter”方法-修改字段或字段引用的对象的方法。
  2. 使所有字段final和private。
  3. 不允许子类覆盖方法。最简单的方法是将该类声明为final。一种更复杂的方法是private在工厂方法中构造构造函数并构造实例。
  4. 如果实例字段包含对可变对象的引用,则不允许更改这些对象:

    • 不要提供修改可变对象的方法。
      • 不要共享对可变对象的引用。永远不要存储对传递给构造函数的外部可变对象的引用;如有必要,请创建副本,并存储对副本的引用。同样,在必要时创建内部可变对象的副本,以避免在方法中返回原始对象。

使用日期对象定义自定义不可变对象 java.util*中的Date对象不是线程不可变的。

好消息是,JDK 8之后的新java.time。* API将 Date 对象作为不可变对象。例如,在下面的示例中,即使属性dateJoined 是 Date object ,Student对象也是不可变的 。

import java.time.LocalDate;

final class Student {
    private final String name;
    private final int age;
    private final LocalDate dateJoined;

    public Student(String name, int age, LocalDate dateJoined) {
        this.name = name;
        this.age = age;
        this.dateJoined = dateJoined;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public LocalDate getDateJoined() {
        return dateJoined;
    }
}

public class TestImmutable {
    @Test
    public void testImmutableObject() {
        Student original = new Student("Yogen", 23, LocalDate.of(2016, 5, 1));

        LocalDate modifiedLocalDate = original.getDateJoined().plusYears(2);

        Student expected = new Student("Yogen", 23, LocalDate.of(2016, 5, 1));
        assertEquals(expected, original);
    }
}

上面示例的代码在GitHub上可用。

不变对象的好处

* 不可变对象无副作用,因此更易于构造,测试和使用。
* 它们更容易缓存,因为对对象的修改不会影响其原始状态。
* 真正不可变的对象始终是线程安全的
* 避免身份可变性问题
* 它们有助于避免时间耦合


原文链接:https://codingdict.com/