为什么String类在Java中声明为final?

问题

从我得知classjava.lang.String在Java中被宣布为final时,我想知道为什么会这样?我当时没有找到任何答案,但是这篇文章:How to create a replica of String class in Java?提醒我了解我的问题。

当然,String提供了我所需要的所有功能,并且从未想过任何需要扩展String类的操作,但是你仍然永远不会知道某人可能需要什么!

那么,有没有人知道设计师在决定最终时的意图是什么?


#1 热门回答(84 赞)

将字符串实现为不可变对象非常有用。你应该阅读aboutimmutability以了解有关它的更多信息。

388956939的一个优点是不可变对象**

你可以通过将重复项指向单个实例来共享重复项。

(fromhere)。

如果String不是最终的,你可以创建一个子类,并且当"被视为字符串"时有两个看起来很相似的字符串,但这实际上是不同的。


#2 热门回答(59 赞)

This is a nice article概述了上述答案中已经提到的两个原因:

  • 安全性:系统可以分发只读信息的敏感位,而不必担心它们会被改变
  • 性能:不可变数据在使线程安全方面非常有用。

这可能是该文章中最详细的评论。它与Java中的字符串池和安全问题有关。它关于如何决定进入字符串池的内容。假设两个字符串相等,如果它们的字符序列相同,那么我们首先会遇到一个竞争条件,以及它的安全问题。如果没有,那么字符串池将包含冗余字符串,从而失去了首先使用它的优势。只是为自己读一读,好吗?

扩展字符串会对平等和实习造成严重破坏。 JavaDoc说等于:

将此字符串与指定对象进行比较。当且仅当参数不为null并且是表示与此对象相同的字符序列的String对象时,结果才为真。

假设java.lang.String未定,aSafeString等于aString,反之亦然;因为它们代表相同的字符序列。

如果将intern应用于aSafeString会发生什么情况 - SafeString会进入JVM的字符串池吗?然后,对于JVM的生命周期,所有对于SafeStringheld引用的对象将被锁定到位。你会得到一个竞争条件,关于谁可能是第一个实习一系列角色的人 - 也许你的SafeString会赢,也许是aString,或者是由不同的类加载器(因此是不同的类)加载的SafeString

如果你赢得了进入游泳池的比赛,这将是一个真正的单例,人们可以通过反射和secretKey.intern().getClass().getClassLoader()访问你的整个环境(沙盒)。

或者JVM可以通过确保仅向池中添加具体的String对象(并且没有子类)来阻止此漏洞。

如果实现了equals,则SafeString!= StringthenSafeString.intern!= String.intern,并且必须将256863987添加到池中。然后游泳池将成为2668313097而不是<String>的游泳池,所有你需要进入游泳池将是一个新的类加载器。


#3 热门回答(23 赞)

String是不可变的或最终的绝对最重要的原因是它被类加载机制使用,因此具有深刻和基本的安全方面。

如果String是可变的或不是最终的,加载"java.io.Writer"的请求可能已被更改为加载"mil.vogoon.DiskErasingWriter"

参考:Why String is immutable in Java