在 Java 里,String 无处不在:JSON、SQL、日志、配置……字符串创建频繁,如果每次都分配新对象,会带来明显的内存与 GC 压力。于是 JVM/类加载体系为 字符串字面量提供了一个复用池:字符串常量池(String Intern Pool)

字符串在堆内存中的存储结构 ⭐

字面量:直接走常量池复用

String a = "abc";
String b = "abc";
System.out.println(a == b); // true(同一份池对象)

在这里插入图片描述

  • 当执行 String a= "abc" 时,JVM 会先去 StringTable 查找是否已有 “abc”。如果没有,则在堆中创建对象,并将其引用存入 StringTable;如果有,则直接返回已存在的引用。
String a = "abc"
String b = "ab";
System.out.println(a == b); // true(同一份池对象)

在这里插入图片描述

new String(“abc”):堆上再来一份

String a = "abc";
String b = new String("abc");
System.out.println(a == b); // false
System.out.println(a.equals(b)); // true

在这里插入图片描述

  • 执行 String b = new String("abc"); 时,JVM 会直接在堆中创建新的 String 对象,不会自动去 StringTable 里查找或存储。

常见写法到底创建几个字符串对象?

同样字面量多次出现

String s1 = "abc";
String s2 = "abc";
String s3 = "abc";

在这里插入图片描述

new String(“abc”) 出现多次

String s4 = new String("abc");
String s5 = new String("abc");

在这里插入图片描述

常量拼接(编译拼接)

new String(“a” + “b”)和 “a” + “b”

String s6 = new String("a" + "b");
String s7 = "a" + "b";
  • String s6 = new String("a" + "b");等价于 String s6 = "ab";

  • String s7 = "a" + "b"; 等价于 String s7 = "ab";

  • 可以通过 javap -verbose 反编译这段代码来验证:

    // 源代码
    String s7 = "a" + "b";
    String s8 = "ab";
    

    反编译字节码展示(关键部分):

    0: ldc           #2   // String ab
    2: astore_1
    3: ldc           #2   // String ab
    5: astore_2
    

    可以看到,s7s8 的字节码指令完全一样,都是 ldc #2(将常量池中编号为 #2 的常量 “ab” 推入栈顶)。这证明编译器已经把 "a" + "b" 直接优化成了 "ab"

final 常量拼接:“a” + “b”

final String s9 = "a";
String s10 = s9 + "b";
  • 等价于 String s10 = "ab";

变量拼接(运行拼接)

s1+s2

String s11= "a";
String s12 ="b";
String s13 = s11 + s12;

等价于

String s11 = "a";
String s12 = "b";
String s13 = new StringBuilder()
                .append(s11)
                .append(s12)
                .toString();

new String(“a”) + “b”;

String s14 = new String("a") + "b";

等价于

String s14 = new StringBuilder()
                .append(new String("a"))
                .append("b")
                .toString();

new String(“a”) + new String(“b”)

String s15= new String("a") + new String("b")

等价于

String s15 = new StringBuilder()
                .append(new String("a"))
                .append(new String("b"))
                .toString();

intern():让等值字符串指向池里的那一个

String x = new String("123"); // 堆对象
String y = x.intern();        // 返回池中对应对象的引用
System.out.println(y == "123"); // true

理解一句话就够了:intern() 返回常量池中等内容字符串的引用;如果池里没有,可能把当前字符串(或其等价表示)放进去,再返回池引用。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐