Java中的字符串池

11

Java有字符串池,因此String类的对象是不可变的。

但我的问题仍然存在 -

为什么需要创建String POOL?

为什么String类不能像其他类一样持有自己的值?

是否内部JVM需要一些字符串或者这是性能上的好处。如果是,那么如何实现的呢?


请参考以下链接:https://dev59.com/1nI-5IYBdhLWcg3wYnSQ,https://stackoverflow.com/questions/3653255/some-queries-regarding-java-string-pool,https://dev59.com/elXTa4cB1Zd3GeqP3qQj,你会得到答案。简而言之,答案是“巧妙地使用JVM内存”。 - Azodious
想一想,如果你想自己创建一个不可变类,你会怎么做? - Azodious
我的问题是 - Java 中有这么多其他的不可变类,为什么 Java 设计者决定创建字符串池。这是为了帮助性能还是 Java 设计的需要? - Amol Ghotankar
4个回答

7

由于字符串是不可变的,所以可以使用池。但是字符串的不可变性不仅仅因为池而决定。不可变性还有许多其他好处。顺便说一下,Double 也是不可变的,但没有 Double 的池。

需要字符串池是为了减少程序使用的所有字符串字面量(和 interned 字符串)占用的内存,因为这些字面量很可能在程序的许多地方被多次使用。你只需引用同一个字符串对象,而不是拥有成千上万个相同的字符串字面量副本,这样可以减少内存使用。

请注意,String 类与其他类没有区别:它持有自己的 char 数组。当调用 substring 时,它也可能与其他 String 实例共享该数组。


Double也是不可变的,而且没有Double对象池。不错的选择.. :) 谢谢 - Aman Gupta
“String literals a program makes”是什么意思?您是说即使应用程序不使用文本并且不明确创建许多字符串对象,JVM内部也会生成许多类似的字符串吗? - James
1
不,我的意思恰恰相反:如果200个类在它们的代码中使用字符串字面量“”,那么只创建一个实例,存储在字符串池中,并由所有这些类共享。 - JB Nizet

0

当编译器发现需要创建一个新的字符串字面量时,它首先检查池中是否存在相同的字符串,如果找到则不会创建新的字符串字面量,而是引用现有的字符串。


编译器实际上与此无关。字面量被放置在类文件中,并在加载类时由池解析。 - Ernest Friedman-Hill
1
那么你的意思是,如果找到匹配项,池中的字符串不会被引用? - Harinder

0

将字符串设为不可变的好处是安全性特性。请阅读以下内容

为什么Java中的字符串被设置为不可变?

尽管性能也是一个原因(假设您已经了解了维护内部字符串池以确保多次使用相同的字符串对象而无需创建/回收它们的原理),但Java中将字符串设置为不可变的主要原因是“安全性”。惊讶吗?让我们了解一下为什么。

假设您需要打开一个需要用户进行身份验证的安全文件。假设有两个名为“user1”和“user2”的用户,他们分别拥有自己的密码文件“password1”和“password2”。显然,“user2”不应该访问“password1”文件。

正如我们所知道的,在Java中,文件名是通过使用字符串指定的。即使您创建了一个“File”对象,您也只能将文件名作为字符串传递,并且该字符串在File对象内部作为其成员之一维护。

如果String是可变的,'user1'可以使用自己的凭据登录,然后在JVM实际执行本机操作系统调用打开文件之前,以某种方式成功将密码文件名(一个String对象)从'password1'更改为'password2'。这将允许'user1'打开user2的密码文件。可以理解的是,这将导致Java中的一个严重安全漏洞。我知道这里有很多“可能会”的情况,但您肯定会同意,这将打开一个门,允许开发人员故意或无意地破坏许多资源的安全性。
由于String是不可变的,JVM可以确保相应File对象的文件名实例成员仍指向同一未更改的“filename”String对象。File类中的'filename'实例成员是'final',因此无论如何都不能修改为指向任何其他String对象,指定任何其他文件而不是预期的文件(即用于创建File对象的文件)。

这并不是真正的安全原因。如果Java中的字符串是可变的,那么File类只需要将文件名的字符串复制到一个新的String对象中即可。这只是一个性能特性,不需要进行复制(对于程序员来说也是一种方便的特性,他们不会忘记所需的复制)。 - Philipp Wendler

0
“为什么需要创建字符串池?”
在创建时,一个字符串对象被存储在堆中,而在构造函数中传递的字符串字面量则被存储在字符串池中。这就是为什么使用字符串对象不是一个好习惯,因为它会创建两个对象。
String str = new String("stackoverflow");

上面的str保存在堆中,使用引用str,构造函数中的字符串文字 -“stackoverflow”- 存储在字符串池中。这对性能不利,会创建两个对象。
流程:创建一个字符串文字 -> JVM查找字符串池中是否存在相同的值(没有对象返回) -> 没有找到该值 -> 创建字符串文字作为新对象(内部使用new关键字) -> 但现在它不会被发送到堆中,而是发送到字符串池中。
区别在于使用new关键字创建对象的地方。如果由程序员创建,它将直接、无延迟地将对象发送到堆中。如果是在内部创建,它将被发送到字符串池中。这是通过方法intern()完成的。在声明一个字符串文字时,intern()会在内部调用。当此方法搜索SP以返回现有字符串对象的引用和/或将对象发送到SP时。
使用new创建字符串对象时,不会调用intern(),而是将对象存储在堆中。但是您可以在String对象上调用intern():String str = new String().intern();现在,str对象将存储在SP中。 例:
String s1 = new String("hello").intern();
String s2 = "hello";
System.out.println(s1 == s2); // true , because now s1 is in SP

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