admin管理员组

文章数量:1031966

Java 中的不可变对象

1. 概述

在本教程中,我们将学习是什么使对象不可变,如何在 Java 中实现不可变性,以及这样做有什么好处。

2. 什么是不可变对象?

不可变对象是其内部状态在完全创建后保持不变的对象。

这意味着不可变对象的公共 API 保证了它在其整个生命周期中的行为方式相同。

如果我们看一下类 String,我们可以看到,即使它的 API 似乎通过其replace方法为我们提供了可变行为,原始字符串也不会改变:

代码语言:javascript代码运行次数:0运行复制
String name = "testjack";
String newName = name.replace("jack", "----");

assertEquals("testjack", name);
assertEquals("test----", newName);Copy

API 为我们提供了只读方法,它绝不应包含更改对象内部状态的方法。

3. Java中的final 关键字

在尝试在 Java 中实现不变性之前,我们应该讨论final关键字。

在 Java 中,变量默认是可变的,这意味着我们可以更改它们保存的值。

通过在声明变量时使用final关键字,Java 编译器不允许我们更改该变量的值。相反,它将报告编译时错误:

代码语言:javascript代码运行次数:0运行复制
final String name = "baeldung";
name = "bael...";Copy

请注意,final仅禁止我们更改变量持有的引用,它不会保护我们使用其公共 API 更改它引用的对象的内部状态:

代码语言:javascript代码运行次数:0运行复制
final List<String> strings = new ArrayList<>();
assertEquals(0, strings.size());
strings.add("testjack");
assertEquals(0, strings.size());Copy

第二个assertEquals将失败,因为将元素添加到列表中会更改其大小,因此,它不是一个不可变的对象。

4. Java中的不可变性

现在我们知道了如何避免更改变量的内容,我们可以使用它来构建不可变对象的 API。

构建不可变对象的 API 需要我们保证无论我们如何使用其 API 都不会更改其内部状态。

朝着正确方向前进的一步是在声明其属性时使用final

代码语言:javascript代码运行次数:0运行复制
class Money {
    private final double amount;
    private final Currency currency;

    // ...
}Copy

请注意,Java保证了金额的值不会改变,所有基元类型变量都是如此。

但是,在我们的示例中,我们只能保证货币不会更改,因此我们必须依靠货币API 来保护自身免受更改。

大多数时候,我们需要对象的属性来保存自定义值,而初始化不可变对象的内部状态的地方是它的构造函数:

代码语言:javascript代码运行次数:0运行复制
class Money {
    // ...
    public Money(double amount, Currency currency) {
        this.amount = amount;
        this.currency = currency;
    }

    public Currency getCurrency() {
        return currency;
    }

    public double getAmount() {
        return amount;
    }
}Copy

正如我们之前所说,为了满足不可变 API 的要求,我们的Money类只有只读方法。

使用反射 API,我们可以打破不可变性并更改不可变对象。但是,反射违反了不可变对象的公共 API,通常,我们应该避免这样做。

5. 好处

由于不可变对象的内部状态在时间上保持不变,因此我们可以在多个线程之间安全地共享它。

我们也可以自由使用它,引用它的对象都不会注意到任何差异,我们可以说不可变的对象没有副作用。

6. 结论

不可变对象不会及时更改其内部状态,它们是线程安全的,并且没有副作用。由于这些属性,不可变对象在处理多线程环境时也特别有用。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。 原始发表:2023-02-22,如有侵权请联系 cloudcommunity@tencent 删除反射教程java变量对象

Java 中的不可变对象

1. 概述

在本教程中,我们将学习是什么使对象不可变,如何在 Java 中实现不可变性,以及这样做有什么好处。

2. 什么是不可变对象?

不可变对象是其内部状态在完全创建后保持不变的对象。

这意味着不可变对象的公共 API 保证了它在其整个生命周期中的行为方式相同。

如果我们看一下类 String,我们可以看到,即使它的 API 似乎通过其replace方法为我们提供了可变行为,原始字符串也不会改变:

代码语言:javascript代码运行次数:0运行复制
String name = "testjack";
String newName = name.replace("jack", "----");

assertEquals("testjack", name);
assertEquals("test----", newName);Copy

API 为我们提供了只读方法,它绝不应包含更改对象内部状态的方法。

3. Java中的final 关键字

在尝试在 Java 中实现不变性之前,我们应该讨论final关键字。

在 Java 中,变量默认是可变的,这意味着我们可以更改它们保存的值。

通过在声明变量时使用final关键字,Java 编译器不允许我们更改该变量的值。相反,它将报告编译时错误:

代码语言:javascript代码运行次数:0运行复制
final String name = "baeldung";
name = "bael...";Copy

请注意,final仅禁止我们更改变量持有的引用,它不会保护我们使用其公共 API 更改它引用的对象的内部状态:

代码语言:javascript代码运行次数:0运行复制
final List<String> strings = new ArrayList<>();
assertEquals(0, strings.size());
strings.add("testjack");
assertEquals(0, strings.size());Copy

第二个assertEquals将失败,因为将元素添加到列表中会更改其大小,因此,它不是一个不可变的对象。

4. Java中的不可变性

现在我们知道了如何避免更改变量的内容,我们可以使用它来构建不可变对象的 API。

构建不可变对象的 API 需要我们保证无论我们如何使用其 API 都不会更改其内部状态。

朝着正确方向前进的一步是在声明其属性时使用final

代码语言:javascript代码运行次数:0运行复制
class Money {
    private final double amount;
    private final Currency currency;

    // ...
}Copy

请注意,Java保证了金额的值不会改变,所有基元类型变量都是如此。

但是,在我们的示例中,我们只能保证货币不会更改,因此我们必须依靠货币API 来保护自身免受更改。

大多数时候,我们需要对象的属性来保存自定义值,而初始化不可变对象的内部状态的地方是它的构造函数:

代码语言:javascript代码运行次数:0运行复制
class Money {
    // ...
    public Money(double amount, Currency currency) {
        this.amount = amount;
        this.currency = currency;
    }

    public Currency getCurrency() {
        return currency;
    }

    public double getAmount() {
        return amount;
    }
}Copy

正如我们之前所说,为了满足不可变 API 的要求,我们的Money类只有只读方法。

使用反射 API,我们可以打破不可变性并更改不可变对象。但是,反射违反了不可变对象的公共 API,通常,我们应该避免这样做。

5. 好处

由于不可变对象的内部状态在时间上保持不变,因此我们可以在多个线程之间安全地共享它。

我们也可以自由使用它,引用它的对象都不会注意到任何差异,我们可以说不可变的对象没有副作用。

6. 结论

不可变对象不会及时更改其内部状态,它们是线程安全的,并且没有副作用。由于这些属性,不可变对象在处理多线程环境时也特别有用。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。 原始发表:2023-02-22,如有侵权请联系 cloudcommunity@tencent 删除反射教程java变量对象

本文标签: Java 中的不可变对象