【发布时间】:2015-07-24 10:30:42
【问题描述】:
我正在做一项作业,我得到了以下问题。
使用toString()方法创建的字符串对象在内存中存储在哪里?
String a = Integer.toString(10);
- 在常量池中
- 在堆上(新操作符对象的区域)
【问题讨论】:
我正在做一项作业,我得到了以下问题。
使用toString()方法创建的字符串对象在内存中存储在哪里?
String a = Integer.toString(10);
【问题讨论】:
它们都进入堆内存。只有 字符串字面量 和 interned 字符串进入字符串常量池。
唯一的例外是像String这样的类:
public String toString() {
return this;
}
它只返回当前字符串(如果它在堆上,它从堆中返回/如果它在字符串常量池中,它从字符串常量池返回)
注意:如果toString() 未被覆盖以显式返回字符串文字,则字符串表示(ClassName@hexValueOfHashCode)总是在堆上创建。 p>
【讨论】:
Boolean 返回一个文字。
toString() 方法,而不是静态方法Integer.toString()。还是您的意思是两者都适用?
toString() 不保证实习字符串并返回它们..
这取决于您调用的toString() 方法的实现。
toString() 方法创建 String 对象,因此取决于它如何执行此操作(以及它是否实习字符串),返回的 String 将在字符串池中与否。
请注意,toString() 只是一个与任何其他方法一样的方法。没有任何魔法可以以任何特殊方式处理toString() 方法的返回值。 (例如,toString() 返回的字符串不会自动实习)。它的工作方式与任何其他返回 String 的方法完全相同。
【讨论】:
这是一个奇怪的(我会说很糟糕)面试问题,因为这个问题的答案取决于 JDK 的实现。这个实现是可以改变的,不是普通人应该知道的,因为要确定,你需要询问实现者或阅读他们的源代码。此外,从 Java 7 开始,字符串池(我假设这是“常量池”的意思)在堆上。所以即使它在字符串池中,它仍然在堆上。
目前(OpenJDK 8u40-b25),String 将是在堆内存中创建的新对象:
public static String toString(int i) {
if (i == Integer.MIN_VALUE)
return "-2147483648";
int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
char[] buf = new char[size];
getChars(i, size, buf);
return new String(buf, true);
}
因为您传递的是10,而不是Integer.MIN_VALUE,所以会创建一个新的String 对象。这个字符串的内容是一个char 数组,它是用new 创建的,所以存在于堆上。 intern() 没有被调用,所以它没有被放入字符串池中。
您可以通过使用 == 和 Strings 测试它们是否是同一个对象而不是同一个值这一事实来测试您得到的行为。以下将评估为False,因为静态Integer.toString() 方法在每次调用时都会在堆上创建一个新的String 对象:
Integer.toString(10) == Integer.toString(10)
如果它们被实习到字符串池中,它们将是同一个对象(因为这就是实习的重点——每个字符串只有一个对象)。以下计算结果为True:
Integer.toString(10).intern() == Integer.toString(10).intern()
请注意,这是专门针对静态 Integer.toString() 方法的答案,而不是针对所有 toString() 方法的答案。例如Boolean.toString(),从字符串池中返回字符串。
【讨论】:
给你的问题是有缺陷的。
首先,两个选项:(1)返回一个常量池中的字符串,和(2)返回一个新创建的字符串,不是互斥的,因为一个方法可以创建一个新的字符串,但是@ 987654321@将其放入常量池中。
其次,对于toString() 方法如何提供它们的字符串,没有特殊要求。这完全取决于特定 toString() 方法的特定实现。你不能一概而论地说他们都做某件事。
某些方法可能会在每次调用时返回 new String(...)(例如,StringBuilder)。
有些方法可能总是从一小组固定的可能性中返回一个字符串实例,而这些固定的可能性可能也在常量池中(适用于Boolean或对于枚举常量)。
某些方法可能会混合使用。 Integer.toString(int) 可能会使用一组预定义的常量来表示小的常见值,但会返回新的堆字符串以获得更模糊的值。或者它可能会按需生成和缓存字符串,这样Integer.toString(10) 可以第一次返回一个新的堆字符串,但在后续调用时返回 same 堆字符串;在这种情况下,如果 Integer.toString 某个地方的调用者 .intern()s 的返回值,那么 Integer 类的按需缓存实例也可以进入常量池。
行为也可能因 JVM 风格或 JVM 版本或传递给它的运行时选项而异。
关于Integer.toString(int) 目前在 OpenJDK 8 中做了什么的具体问题,check the source:
public static String toString(int i) {
if (i == Integer.MIN_VALUE)
return "-2147483648";
int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
char[] buf = new char[size];
getChars(i, size, buf);
return new String(buf, true);
}
事实证明,它为Integer.MIN_VALUE 返回一个字符串文字(将在常量池中),但为每个其他值返回一个新的堆字符串。 (这可能看起来很奇怪,但在许多编程语言中,数字到字符串的函数通常会以这种方式对最小值进行例外处理,因为对于整数的two's complement 表示,最大值比绝对值小 1最小值(例如,-2,147,483,648 到 +2,147,483,647 的范围),这意味着最小值是唯一不能翻转为正值然后使用与正值相同的共享循环代码进行处理的负值。)
问题的第三个问题是答案几乎不重要。只有当您发现 toString() 方法的实现不起作用或性能不令人满意时,您才能调查原因,否则,只要它有效且有效地工作,为什么有人要关心它如何缓存字符串或是否是否缓存它们? Java 新手过于担心愚蠢的字符串常量池的细节,这是 Java 决定不为对象使用 == 调用 .equals() 的一个不幸的副作用,这使得常量的行为池更明显。
当您需要一个肯定在常量池中的字符串时,您可以自己实习,或者将其写为源中的字符串文字。否则,你无法保证它是否在常量池中,也不需要这样的保证。
基于Integer.toString(int) 的实现,他们可能正在寻找答案2(“在堆上”),但这是对一个有缺陷的问题的有缺陷的答案。
【讨论】:
我认为这应该取决于实施。但是对于来自java.lang.Object 的默认 toString 和许多其他对象的toString 实现仅返回字符串文字。所以我可以告诉你,它们在字符串常量池中,因为默认情况下它们是interned,并且所有内部字符串都将存储在字符串常量池中。
示例 1
public String toString() {
return "my test string"; // will be in string constant pool
}
示例 2
public String toString() {
String s1 = new String("my test string");
return s1; // s1 will be in heap
}
当然我不必告诉你s1.intern() 会将示例字符串放入字符串常量池中(如果该字符串尚不可用)
【讨论】:
toString() 不是interned。为什么toString() 只返回文字?。
Object.toString() 返回文字显然是错误的。试试new Object().toString(),你会看到的。
s == s.intern(),也不一定意味着s 在调用intern() 之前已经被拘留。
在 Integer 包装类中,一旦给定的 int 被解析为 char,它将返回一个新的 String 对象。
return new String(buf, true);
这将在堆内存中创建 1 个字符串对象。
引用变量“a”将指向堆(普通内存)中的对象。
但请记住,
String str = new String("abc");
将创建 2 个对象,一个在堆中,另一个在字符串池中。
编译时常量 String 类型的表达式总是被保留的。
【讨论】:
它依赖于实现。但是默认情况下,字符串会转到字符串池。
案例一:
public String toString() {
return "Constant Pool"; // String constant pool
}
案例 2:
public String toString() {
String string2 = new String("Heap String");
return string2; // in heap
}
两个表达式都给你String 对象,但它们之间存在细微差别。
new() 运算符创建String 时,它总是会在heap memory 中创建一个新对象。 当您使用 String literal 表示法创建字符串时,它会自动调用
intern()方法将该对象放入 String pool(如果它不在池中)。如果是 new,它不会自动发生,直到对该对象调用intern()方法。
【讨论】:
Integer.toString()。