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

2022-08-31 07:48:55

当我得知该类在Java中被宣布为最终类时,我就想知道为什么会这样。我当时没有找到任何答案,但是这篇文章:如何在Java中创建String类的副本?让我想起了我的查询。java.lang.String

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

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


答案 1

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

不可变对象的一个优点是

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

从这里开始)。

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


答案 2

这是一篇不错的文章,概述了上述答案中已经提到的两个原因:

  1. 安全性:系统可以分发敏感的只读信息,而不必担心它们会被更改
  2. 性能:不可变数据对于使事情变得线程安全非常有用。

这可能是那篇文章中最详细的评论。它与Java中的字符串池和安全问题有关。它是关于如何决定什么进入字符串池。假设两个字符串相等,如果它们的字符序列相同,那么我们有一个竞争条件,即谁先到达那里,以及随之而来的安全问题。如果不是,则字符串池将包含冗余字符串,从而失去首先拥有它的优势。就自己读出来吧?


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

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

假设不是最终的,a 可以等于 a,反之亦然;因为它们将表示相同的字符序列。java.lang.StringSafeStringString

如果您申请了 -- 会进入 JVM 的字符串池吗?然后,在 JVM 的生存期内,保存引用的对象 和所有对象都将锁定到位。你会得到一个竞争条件,关于谁可能是第一个插入一系列字符的人 - 也许你会赢,也许是一个,或者可能是一个由不同的类加载器加载的(因此是一个不同的类)。internSafeStringSafeStringClassLoaderSafeStringSafeStringStringSafeString

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

或者,JVM 可以通过确保只将具体的 String 对象(而不是子类)添加到池中来阻止此孔。

如果等于实现为 != 则 != ,并且必须添加到池中。然后,池将变成一个池,而不是池,你只需要一个新的类加载器进入池。SafeStringStringSafeString.internString.internSafeString<Class, String><String>