当前位置: 首页 > 知识库问答 >
问题:

使用“”运算符的非字符串操作数后,生成的字符串是否会进入字符串池?

秦阳旭
2023-03-14

我正在学习和准备OCP 1Z0-815,我从这本优秀的准备书中读到:

Deshmukh,Hanumant。OCP Oracle认证专业JavaSE 11程序员I考试基础1Z0-815:通过OCPJava11开发者认证第1部分考试1Z0-815的学习指南(p.99)。Enthuware.电子书做Kindle。

String str = "hello";
for( int i = 0; i < 5; i + +) {
    str = str + i;
} 

上面创建了一个字符串对象,在循环的开始处包含“hello”,然后在每次迭代中再创建两个,一个字符串包含int值i和连接的字符串。因此,总的来说,上面的代码创建了12*5=11个字符串。然而,如果你被问到有多少字符串可以被垃圾收集,答案并不是那么简单。Java语言规范在第15.8.1节中提到,运算符的非字符串操作数在运行时转换为字符串,但它没有明确说明该字符串是否进入字符串池(在这种情况下,它不会被垃圾收集)。

让我给你看另一段代码

String s = "hello";
int k = 100;
s = s + " 123" + k;

在这种情况下,JLS第15.8.1节明确指出,编译器可以通过使用StringBuilder避免创建多个字符串。因此,不可能知道将创建多少字符串,以及有多少字符串有资格被垃圾收集。。。

上面的语句“……Java语言规范在第15.8.1节中提到,运算符的非字符串操作数在运行时被转换为字符串,但它没有明确说明该字符串是否进入字符串池……”驱使我四处搜索,但我没有找到一个解释:因为我没有看到“new”,我知道它确实会进入字符串池,所以它不是一个对象。因此,垃圾收集一点也不可悲。我说错什么了吗?

换句话说,据我们所知,每个循环都会在堆中的字符串池中产生一个常量字符串(心目中的是Java 11,而不是Java 7)。hello0、hello1、hello2等等,而且根本不会有任何垃圾收集的候选对象。我知道垃圾收集是由new操作符创建的“clean”对象,它不作用于字符串池。

根据结论段落,“……第15.8.1节明确指出,编译器可以通过使用StringBuilder避免创建多个字符串。因此,无法判断将创建多少字符串,以及有多少字符串有资格被垃圾收集……”我假设它是说,在第二个代码示例(wihtout循环)中,我无法发现在字符串池中创建了多少字符串,因为StringBuilder是在后台使用的,我们知道StringBuilder在内存中操纵串联,避免创建许多字符串文字。如果是这样的话,我仍然可以在第一个代码中假设每个循环都会产生一个文本字符串(hello0,hello1…)这将进入字符串池,实际上不符合垃圾收集的条件。我错了吗?

注:你可能会评论垃圾收集作用于字符串池,但我理解在实践中,字符串池中的文字字符串驻留这么长时间,我们可以认为它在程序结束之前永远不适合垃圾收集(“在使用文字的每种方法的代码中都有一个隐式引用字符串对象”。和“字符串池中的所有字符串文本都是可访问的,直到程序终止,因此不符合垃圾收集的条件”

共有1个答案

段干靖
2023-03-14

就我看不到的new而言,我知道它确实会进入字符串池,因此它不是一个对象。

首先,Java语言规范没有特别提到字符串池。它实际上说的是,如果两个字符串值常量表达式相等,那么它们将是同一个对象。(这包括字符串文字,但也包括string s=“a”b”等情况)

弦乐池是

新的怎么样?

JLS还表示,new操作员总是产生一个全新的对象:

类实例创建表达式的值是对指定类的新创建对象的引用。每次计算该表达式时,都会创建一个新对象(JLS 15.9.4)

这样做的后果是:

String a1 = new String("a");
System.out.println("a" == a1);   // prints "false"

必须这样做,因为新的字符串不是新字符串。由于字符串池是一种重复数据消除机制,我们可以得出结论,new string(“a”)没有将新字符串对象放入字符串池。

同样地,操作符创建一个新字符串:

“字符串串联的结果是对两个操作数字符串串联的string对象的引用。在新创建的字符串中,左操作数的字符先于右操作数的字符。”(JLS 15.18.1)

JLS还说编译器可以优化涉及的表达式:

“一个实现可以选择在一个步骤中执行转换和连接,以避免创建然后丢弃一个中间字符串对象。为了提高重复字符串连接的性能,Java编译器可以使用StringBuffer类或类似的技术来减少中间 对象的数量de>String 通过计算表达式创建的对象。

对于基元类型,实现还可以通过直接从基元类型转换为字符串来优化包装对象的创建。"(JLS 15.18.1)

但请注意,这种优化只允许在一个表达式中进行,而不允许跨多个语句进行。

JLS没有提到其他字符串操作。对于它们的规范,我们需要参考javadocs。在当前版本中,唯一声明始终创建新字符串的String方法是两个join方法。

但同样,唯一专门提到字符串池的字符串方法是intern方法。

这就是说明书上说的。字符串实现实际上做什么?

如果你回顾一下Java 1.1中Java SE实现的标准源代码,你会发现除了intern,没有字符串方法将对象放入字符串池。没有一个

你也是这么说的:

我理解,实际上,字符串池中的文字字符串驻留这么长时间,我们可以认为它在程序结束之前永远不符合垃圾回收的条件。

大多数情况下都是这样。例外情况是,您可以创建一个类加载器,并使用它动态加载类。如果该类加载器变得不可访问,并且没有对其加载的类的其他引用,则其字符串文本可能无法访问。。。在节目结束之前。

如果您使用可以热加载新版本类的产品(例如网络容器),这很容易发生。

最后,如果我们看一下这个例子:

   String str = "hello";
   for (int i = 0; i < 5; i++) {
       str = str + i;
   } 

实际上:

>

在每个循环迭代中,至少会创建一个新的String。可能会创建一个中间的String来表示i的字符串值,但是编译器可以优化它。

所有中间String对象都不会被插入/添加到字符串池中。

"Hello"字面值和最终值之外的所有内容都将无法访问。

 类似资料:
  • 本文向大家介绍通过+(字符串串联)运算符进行字符串串联。,包括了通过+(字符串串联)运算符进行字符串串联。的使用技巧和注意事项,需要的朋友参考一下 您可以使用Java的“ +”运算符来连接字符串。 示例 输出结果

  • SETRANGE key offset value 用value 参数覆写(overwrite)给定key 所储存的字符串值,从偏移量offset 开始。 不存在的key 当作空白字符串处理。可以用作append: 注意: 如果偏移量>字符长度, 该字符自动补0x00,注意它不会报错

  • substr key start end 返回截取过的key的字符串值,注意并不修改key的值。下标是从0开始的

  • append key value 返回新字符串值的长度。

  • 问题 你想在字节字符串上执行普通的文本操作(比如移除,搜索和替换)。 解决方案 字节字符串同样也支持大部分和文本字符串一样的内置操作。比如: >>> data = b'Hello World' >>> data[0:5] b'Hello' >>> data.startswith(b'Hello') True >>> data.split() [b'Hello', b'World'] >>> dat

  • 对性能是否有影响? 不同Java版本的行为是否有差异?