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

Ale*_*lex 134 java string final

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

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

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

Bru*_*eis 86

将字符串实现为不可变对象非常有用.您应该阅读有关不可变性的内容,以便更多地了解它.

不可变对象的一个优点是

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

(从这里).

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

  • 除非我没有看到最终类和不可变对象之间的连接,否则我看不出你的答案与问题的关系. (65认同)
  • @BrunoReis - 发现了一篇很好的文章,你可以链接到James Gosling(Java的创建者)的访谈,在那里他简要地讨论了这个主题[这里](http://www.artima.com/intv/gosling313.html) .这是一个有趣的片段:"强制Strings不可变的一个原因是安全.你有一个文件打开方法.你传递一个字符串.然后它在进行操作系统之前进行所有类型的身份验证检查如果你设法做了一些有效地改变字符串的事情,在安全检查之后和操作系统调用之前,那么繁荣,你就......" (11认同)
  • 因为如果它不是最终的,你可以将StringChild作为String参数传递给某个方法,并且它可以是可变的(因为子类状态改变). (9认同)
  • @Bruno,re:downvotes:我没有向你投票,但你可以添加一句话来说明如何防止子类强制执行不变性.现在,这是一个半答案. (7认同)
  • 哇!Downvotes?难道你不明白子类化与不变性有什么关系?我很欣赏有关问题的解释. (4认同)
  • 如果String及其数据成员不是final,则可能会被添加了可变性的派生类覆盖.好吧,如果数据成员不是私人的.:) (2认同)
  • 并且因为你无法覆盖(最终)hashCode,实习和相等的方法,这些方法对于所有类库都是基础. (2认同)
  • StringBuilder也是最终的,所以不可变是不必要的原因 (2认同)

Anu*_*rag 59

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

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

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


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

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

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

如果您申请internSafeString- 会SafeString进入JVM的字符串池会发生什么?然后ClassLoader,SafeString保持引用的所有对象将在JVM的生命周期内锁定到位.你会得到一个关于谁可能是第一个实习一系列角色的竞争条件 - 也许你SafeString会赢,也许是一个String,或者可能是SafeString由不同的类加载器(因此是不同的类)加载.

如果你赢得了比赛,这将是一个真正的单身人士,人们可以通过反射和访问你的整个环境(沙箱)secretKey.intern().getClass().getClassLoader().

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

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

  • 当然,性能原因是一个谬误:如果String是一个接口我本来能够提供一个在我的应用程序中表现更好的实现. (2认同)

小智 25

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

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

reference:为什么String在Java中是不可变的


Thi*_*ilo 15

String 在Java中是一个非常核心的类,许多东西依赖于它以某种方式工作,例如是不可变的.

使类final可以防止可能破坏这些假设的子类.

请注意,即使是现在,如果使用反射,也可以破坏字符串(更改其值或哈希码).可以使用安全管理器停止反射.如果String不是final,每个人都可以做到.

其他未声明的类final允许您定义有些破坏的子类(例如,您可能List会将其添加到错误的位置),但至少JVM不依赖于其核心操作.

  • 课堂上的'final`并不能保证不变性.它只是保证一个类的不变量(其中一个可以是不变的)不能被子类改变. (6认同)
  • 最终上课不会让它变得不可动摇.但是,创建一个不可变的类最终确保没有人创建一个破坏不变性的子类.也许那些提出不变性问题的人不清楚他们究竟是什么意思,但他们的陈述在上下文中理解时是正确的. (4认同)
  • @凯文:是的。类的 Final 保证不存在子类。与不变性无关。 (2认同)

Art*_*tur 6

正如布鲁诺所说,这是关于不变性的.这不仅仅是关于字符串,还有关于任何包装器,例如Double,Integer,Character等.这有很多原因:

  • 线程安全
  • 安全
  • 由Java本身管理的堆(与以不同方式收集的垃圾的普通堆不同)
  • 内存管理

基本上它是这样的,作为程序员,您可以确保您的字符串永远不会被更改.它也是,如果你知道它是如何工作的,可以改善内存管理.尝试一个接一个地创建两个相同的字符串,例如"hello".如果您调试,您会注意到它们具有相同的ID,这意味着它们完全是同一个对象.这是因为Java允许你这样做.如果字符串是可变的,这将是不可能的.他们可以拥有相同的等等,因为它们永远不会改变.因此,如果您决定创建1,000,000字符串"hello",那么您真正要做的就是创建1,000,000个指向"hello"的指针.同样地将字符串或任何包装器上的任何函数联合起来将导致创建另一个对象(再次查看对象ID - 它将改变).

Java中的最终版本并不一定意味着对象不能改变(例如C++不同).这意味着它指向的地址不能更改,但您仍然可以更改其属性和/或属性.因此,在某些情况下理解不变性和最终性之间的差异可能非常重要.

HTH

参考文献:

  • 此外,类的final关键字与字段的final关键字完全不同. (2认同)
  • 并非所有的弦乐都会进入那个区域,只有已经被实习过的弦乐.对于文字字符串,实习是自动的.(@Thilo,在您提交评论时输入内容). (2认同)