Java中不可变(immutable)和最终(final)有什么区别?

38

最近有人问了我这个问题。但是我无法简洁地解释这两个概念的区别。

例如:

Final 和 Immutable:

final String name = "John";

如果现在我写下:

name = "Sam";

我将会得到编译错误。

不可变:

String name = "John";
name = "Sam"; 

它有效。

我认为这解释了其中的一部分应用。但是我可以得到一个易于理解的关于这两个主题的解释吗?


1
由于某些原因,我无法标记此问题为重复的副本。 - Mayuso
3
@Mayuso 我认为这两个问题没有关联,请不要标记为重复。我在这里得到了完美的答案。 - nanospeck
9个回答

64
final 表示你不能改变对象的引用指向另一个引用或其他对象,但是你仍然可以修改它的状态(使用 setter 方法)。而 immutable 表示对象的实际值不能被更改,但是你可以将其引用指向另一个对象。
关于你问题的第二部分(不可变性部分),编译器会创建一个具有值 "Sam" 的新 String 对象,并将 name 引用指向它。

谢谢Zouag。非常好的解释。 - Lova Chittumuri

20

final保证对象的地址不会改变,而Immutable则表示一旦创建了对象,就无法更改其状态。

final只是一个关键字,而Immutable是一种模式。

对于您的第一个问题,您将变量标记为final,这意味着您将无法更改其内存地址,并且不能再次赋值。

在第二个问题中,Immutable确保您无法更改所创建的对象的状态。


6
当您将一个字段声明为final时,引用不会更改。它将始终指向同一个对象。
如果对象不是immutable,则可以使用其上的方法来更改对象本身 - 它是相同的对象,但其属性已更改

6

不可变性(Immutable): http://www.programcreek.com/2009/02/diagram-to-show-java-strings-immutability/

当您更改字符串时,会创建新的字符串对象(“abcdef”),并将引用从'abce'更改为'abcdef'。但您不能删除'abcd'。只能更改引用。这就是不可变性。

final:

实际上 final 是一个关键字。当您将其添加到变量时,无法更改引用。


4

final

final关键字表示对象不可更改。

final String s = "Hello";
// Not allowed.
s = "Bye";

不可变性

对象的内容不能被改变。

BigInteger one = BigInteger.ONE;
// Does not change `one` or `BigInteger.ONE`.
one.add(BigInteger.ONE);
// Have to do it this way.
BigInteger two = one.add(one);

2
final String name = "John";

当您编写以上代码时,您的代码告诉编译器引用 name 总是指向相同的内存位置。现在为什么我说内存位置呢?因为引用所指向的对象可能是可变的,比如整数数组或列表。所以如果我说 final int[] arr = {5,6,1};,我可以执行 arr[2] = 3;,但我不能执行 arr = {3,4,5},因为您将尝试将新的 int[] 分配给 final 变量 arr,这是一个新的内存位置,编译器会显示错误。"最初的回答"
String name = "John";
name = "Sam"; 
name 变量是一个 String 类型的变量,由于在 Java 中,String 是不可变的(immutable),这意味着一旦创建了对象,并且即使您将其更改为 Sam,它也是一个不同的对象,被引用的对象 name 的状态无法更改。之前的对象 John 将没有引用,并且如果没有其他指向它的引用,则可以在垃圾收集器运行时进行收集。

2
当你使用关键字“final”时,这意味着你不能更改变量所指向的对象的引用。因此,在这种情况下,变量“name”不能被指向另一个字符串对象。但请注意,由于我们使用了最终引用,因此可以更改对象的内容。此外,Java中的字符串是固有的不可变的。也就是说,你不能更改它的内容。
因此,在你的情况下,final将会对一个字符串对象进行最终引用。由于你不能更改变量以指向另一个字符串对象,代码将失败。
请查看以下代码以了解最终变量的工作原理。
public class test {

public static void main(String[] args) {
    final A aObject = new A();
    System.out.println("Old value :" + aObject.a);
    aObject.a = 2;
    System.out.println("New value :" + aObject.a);
}} 

class A {
public int a = 1;}

非常好的演示。 - Lova Chittumuri

1

Immutable指的是一旦对象的构造函数执行完成,该实例就无法被更改。

这很有用,因为它意味着您可以传递对该对象的引用,而不必担心其他人会更改其内容。特别是在处理并发时,从不更改的对象没有锁定问题。

例如:

class Foo
{
     private final String myvar;

     public Foo(final String initialValue)
     {
         this.myvar = initialValue;
     }

     public String getValue()
     {
         return this.myvar;
     }
}

Foo不必担心调用getValue()的调用者可能会更改字符串中的文本。

如果您想象一个类似于Foo的类,但其成员是StringBuilder而不是String,则可以看到调用getValue()的调用者将能够更改Foo实例的StringBuilder属性。

final是Java中的保留关键字,用于限制用户,可以应用于成员变量、方法、类和局部变量。在Java中,Final变量通常使用static关键字声明,并被视为常量。例如:

public static final String hello = "Hello"; 当我们在变量声明中使用final关键字时,存储在该变量中的值不能在后面更改。

例如:

public class ClassDemo {
  private final int var1 = 3;
  public ClassDemo() {
    ...
  }
}

注意:声明为final的类不能被扩展或继承(即,超类不能有子类)。还要注意,声明为final的方法不能被子类重写。
在这个线程中讨论了使用final关键字的好处。

0

不可变性:String和包装类是不可变的。由于String常量池,它们不能在对象内部更改其值,但可以更改持有不同值的对象的引用。

String s1 = new String("cant't change");

最终结果:当我们创建一个字符串的引用时

Final String s2 = "cant't change";

s2的引用指向一个内部值为“无法更改”的对象。

现在,引用s将始终指向保存值“无法更改”的对象。它的引用不能被更改。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接