声明String并将其声明为final是有区别的吗?

时间:2012-02-25 12:13:23

标签: java string

我知道字符串是不可变的。

然后,有什么区别:

String name ="Name";
final String name ="Name";

为什么我们在这种情况下使用final?因为String已经是不可变的,所以似乎没有必要。第二个相关问题,为什么String immutable?其他数据类型如int,boolean则不是。如果String是不可变的,那么它是否具有线程安全性?我读到“如果String是可变的,加载”java.io.Writer“的请求可能已被更改为加载”mil.vogoon.DiskErasingWriter“” means

6 个答案:

答案 0 :(得分:4)

这里有很多问题,但这里有一些注释应该回答大部分问题:

  • final表示该变量只能分配一次。它与可变性无关,可变性是对象的属性,而不是变量(只是一个句柄)
  • String是不可变的,因为您无法更改其内部状态(它具有保存字符的private char[] chars)。基元也是不可改变的。这在包装器等效物中更明显 - IntegerLong等。
  • 是的,String是线程安全的,因为无论多少线程使用它都无法更改其状态。字符串上的每个操作都会产生一个新实例,而不是更改现有实例。

答案 1 :(得分:2)

final表示您无法为变量分配新值。

String name = "Name";
name = "MyName"; // legal

final String name = "Name";
name = "MyName"; // illegal, compiler error

这是一篇关于为什么字符串是不可变的文章:

http://javarevisited.blogspot.com/2010/10/why-string-is-immutable-in-java.html

答案 2 :(得分:2)

  

我读到“如果String是可变的,加载”java.io.Writer“的请求可能已被更改为加载”mil.vogoon.DiskErasingWriter“”

行。首先要认识到的是,这是一个假设的讨论。字符串不可变。

现在想象一下这段代码:

public static final String CLASS_NAME = "java.io.Writer";

...

Class<?> clazz = Class.forName(CLASS);

这实际上加载了什么类?

如果String是可变的,那么在安全沙箱中运行的一些恶意代码将能够改变CLASS_NAME引用的String对象的内容。特别是,它可以将其从“java.io.Writer”更改为“mil.vogoon.DiskErasingWriter”。最终结果是你的应用程序会被欺骗加载错误的类。

通过使String成为不可变类型(以及其他一两件事),这种攻击机制就会被挫败。

答案 3 :(得分:1)

像威廉说的那样:

  

final意味着您无法为变量分配新值。

此外,如果您使用匿名内部类:

final String mystring = "Hello";
button.addClickHandler(new ClickHandler() {
    void click() {
        System.out.println(mystring);
    }
});

mystring对象必须为最终版。

答案 4 :(得分:1)

它是对象与其引用之间的区别。只能为final变量赋值一次。无论变量是否指向不可变对象,都是如此。

请注意,“不可变”对象本身通常(但不一定)由final个引用组成。

答案 5 :(得分:0)

其他要点已在这里得到解答,但您仍在寻找对您发现的引用的解释:

  

如果String是可变的,则加载“java.io.Writer”的请求可能有   已更改为加载“mil.vogoon.DiskErasingWriter”

作为参考,这似乎来自一个不同的SO答案,这个答案有点短,并且没有详细说明。

通常,如果你编写一个接受可变对象并存储它们的公共类,或者返回它存储的可变对象,那么调用代码可以从该类的脚下改变该对象。

举个例子。免责声明:我对ClassLoader了解不多,所以这只是为了说明问题。 ClassLoader有一个函数loadClass(String name)。想象一下,该方法首先检查该名称的类是有效还是可信,然后加载它。现在,如果String是可变的,调用代码可以在已经检查过类之后从类加载器的脚下更改类名,从而规避安全措施。

当然,如果String是可变的,那么可以希望ClassLoader的实现首先会生成String的防御性副本。这样,调用代码就没有机会绕过安全性,因为它无法修改 copy 的内容。这是一般规则:不要让调用代码对类的可变内部数据有任何处理。

所以String not 需要是不可变的,以确保安全的类加载。但是,对于不可变对象,更容易确保这种安全性,因为它们根本无法从您下面进行更改。