使用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代替。


以上是使用new运算符创建了多少字符串的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>