Java不可变类?

时间:2011-11-23 16:05:02

标签: java

我发现article有一段有趣的代码:

public class Employee {

    private String firstName;
    private String lastName;

    //private default constructor
    private Employee(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public static Employee valueOf (String firstName, String lastName) {
        return new Employee(firstName, lastName);
    }
}

我非常好奇理解创建这种类的优势。 我知道这个类的对象是不可变的,因为一旦初始化就无法改变它的变量值。我之前从未做过这样的事情,我真的不明白它的优点。

  • 为什么这是一个好习惯?
  • 您能说出可以使用此方法的情况吗?
  • 常量或只读变量怎么样?这不是很相似吗?
  • 在文章中说,这对应用程序的性能不利。 但为什么

7 个答案:

答案 0 :(得分:19)

您提到的示例是Immutable Objects。它在programming languages中被广泛使用的概念。

从上面的链接引用。优点是

  • 易于构建,测试和使用
  • 是自动线程安全的,没有同步问题
  • 不需要复制构造函数
  • 不需要克隆
  • 的实现
  • 允许hashCode使用延迟初始化,并缓存其返回值
  • 在用作字段时无需进行防御性复制
  • 制作好的Map键和Set元素(这些对象在集合中不得改变状态)
  • 在施工时建立了一次不变的类,并且永远不需要再次检查
  • 总是有“失败原子性”(Joshua Bloch使用的术语):如果一个不可变对象 - 引发异常,它永远不会处于不受欢迎或不确定的状态

答案 1 :(得分:4)

不可变类是:

  • 默认情况下是线程安全的(并发写不会发生)
  • 可高速缓存

您可以在Effective Java中的Java语言文本中阅读很多相关信息。

答案 2 :(得分:3)

不可变类的主要优点是线程安全。线程的大多数问题来自共享的,可变的状态。通过使对象不可变,可以更容易地推理它们,尤其是在多线程环境中。

文章称“创建不可变对象可能会影响应用程序的性能。”我不确定为什么会这样说。这是完全错误的。可变对象没有固有的东西会影响应用程序的性能。

答案 3 :(得分:3)

  

- 为什么这是一个好习惯?

因为你可以传递这个类,并确保它永远不会被“流氓”代码修改。对于Java Strings来说,它们是不可变的。

  

- 您是否可以说出可以使用此方法的情况?

在许多团队协同工作或设计框架或API的大型项目中,它非常有用。在这些情况下,由于您不对代码的某些部分负责,因此您永远不会相信您传递给代码其他部分的对象不会被更改。如果您需要确保不修改对象,请使用不变性。

  

- 关于常量或只读变量的内容?这不是很相似吗?

不是Java,因为我们既没有const也没有只读。我们所拥有的只是final关键字,确保在第一次分配之后不会修改对象引用。但即使引用不能,仍然可以修改基础对象。不可变类确保在创建后不会更改对象状态。

  

- 在文章中说,这对应用程序的性能不利。但为什么呢?

因为每次需要修改对象时,都需要创建新实例。对于字符串,您不能执行myString.append("42"),您需要执行myString = myString+"42",这会创建一个新的String对象。

答案 4 :(得分:2)

如果你正在使用hashTables,那么拥有不可变对象是好的,因为你不需要在对象状态发生变化时重新计算hashCode(因为它们是不可更改的)。

答案 5 :(得分:1)

文章说:

  

要使类不可变,可以将其所有构造函数定义为private,然后创建一个公共静态方法来初始化和对象并将其返回。

实际上,这是错误的。这两个概念并没有真正相关。

E.g。你可以将Employee类的构造函数声明为public,它仍然是不可变的。

或者您可以将可变对象作为参数传递给工厂方法或声明mutator方法

- >尽管您使用的是工厂方法和私有构造函数,但Employee仍然是可变的。

答案 6 :(得分:0)

在您给出的示例中,他将构造函数设置为私有,从而直接从外部控制对象创建。

含义:由于构造函数是私有的,因此无法执行

Employee e = new Employee("steve","jobs"); 

来自本课程的外部。

通过这样做,这个类的程序员正在将这个类的对象创建到他的控件中。

这非常有用,当你编写非常庞大的服务器端类时,由于它的大小,创建一个对象可能会占用大量内存。现在,您如何保护您的客户,而不是为您的班级创建更多的对象?

上述问题的答案很简单,只需将构造函数设为私有,然后在静态方法中为自己创建对象。 注意:可以使用类名直接访问静态方法。

注意:这种设计模式将大量用于单例设计模式,对于给定的类只需要一个对象。