使用new运算符创建了多少字符串
使用 new 运算符创建了多少字符串。
假设我正在使用 new 运算符创建一个字符串。
String str = new String("Cat")
它会创建 2 个字符串,一个在堆中,另一个在字符串池中?
如果它也创建字符串,string poll那么字符串intern方法的目的是什么?
回答
多少个对象?
它会创建 2 个字符串,一个在堆中,另一个在字符串池中?
当您编写 时"Cat",您最终会填充池Cat并"Cat"调用从池中加载此对象。这通常在编译时就已经发生了。然后new String(...)将创建一个新的字符串对象,完全忽略池。
所以这个片段导致了两个对象的创建。为了消除您的困惑,请考虑以下示例:
String first = "Cat";
String second = "Cat";
String third = "Cat";
String fourth = new String("Cat");
在这里,也创建了两个对象。所有"Cat"调用都会将字符串从池中加载出来,因此first == second == third并且fourth将是它自己的对象,因为它使用了new,这总是会导致创建一个新对象,绕过任何类型的缓存机制。
对象是在堆上还是在堆栈上创建并没有真正定义。内存管理完全取决于 JVM。
字符串池详细信息
对于大多数 Java 实现,字符串池已在编译期间创建和填充。当您编写 时"Cat",编译器会将表示的字符串对象Cat放入此池中,并且"Cat"您的代码中的 将通过从池中加载此对象来替换。当您反汇编编译的程序时,您可以很容易地看到这一点。例如源代码:
public class Test {
public static void main(String[] args) {
String foo = "Hello World";
}
}
拆解(javap -v):
Classfile /C:/Users/Zabuza/Desktop/Test.class
Last modified 30.03.2021; size 277 bytes
SHA-256 checksum 83de8a7326af14fc95fb499af090f9b3377c56f79f2e78b34e447d66b645a285
Compiled from "Test.java"
public class Test
minor version: 0
major version: 59
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #9 // Test
super_class: #2 // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 1
Constant pool:
#1 = Methodref #2.#3 // java/lang/Object."<init>":()V
#2 = Class #4 // java/lang/Object
#3 = NameAndType #5:#6 // "<init>":()V
#4 = Utf8 java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = String #8 // Hello World
#8 = Utf8 Hello World
#9 = Class #10 // Test
#10 = Utf8 Test
#11 = Utf8 Code
#12 = Utf8 LineNumberTable
#13 = Utf8 main
#14 = Utf8 ([Ljava/lang/String;)V
#15 = Utf8 SourceFile
#16 = Utf8 Test.java
{
public Test();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=2, args_size=1
0: ldc #7 // String Hello World
2: astore_1
3: return
LineNumberTable:
line 3: 0
line 4: 3
}
SourceFile: "Test.java"
如你所见,有
#7 = String #8 // Hello World
#8 = Utf8 Hello World
并且方法中的部分被替换为
0: ldc #7
Hello World从字符串池加载。
字符串实习
字符串实习生方法的目的是什么?
好吧,它使您可以根据池中的版本交换字符串。并用您的字符串填充池,以防它之前不存在。例如:
String first = "hello"; // from pool
String second = new String("hello"); // new object
String third = second.intern(); // from pool, same as first
System.out.println(first == second); // false
System.out.println(first == third); // true
用例
不过,我还没有看到此功能的实际应用程序。
但是,我可以想到一个用例,您可以在应用程序中动态创建长字符串,并且您知道它们稍后会再次出现。然后,您可以将字符串放入池中,以便在稍后再次出现时减少内存占用。
因此,假设您从 HTTP 响应中收到一些长字符串,并且您知道大多数情况下这些响应是完全相同的,并且您还希望将它们收集在一个List:
private List<String> responses = new ArrayList<>();
...
public void receiveResponse(String response) {
...
responses.add(response);
}
如果没有实习,您最终会在您的记忆中保留每个字符串实例,包括重复项。但是,如果您实习,则内存中不会有重复的字符串对象:
public void receiveResponse(String response) {
...
String responseFromPool = response.intern();
responses.add(responseFromPool);
}
当然,这有点做作,因为您也可以在Set这里使用 a代替。