【JAVA基础面经】Java 字符串常量池

张开发
2026/4/12 22:57:19 15 分钟阅读

分享文章

【JAVA基础面经】Java 字符串常量池
文章目录前言字符串常量池的位置典型例子常量池中已存在的字符串进行复用new String 会创建新变量new String(“a” “b”)“a” “b”str1 str2stranew String(“ab”) “ab”;new String(“ab”) new String(“ab”)new String(“ab”) new String(“cd”);字符串拼接的底层原理intern( ) 函数前言有下面的一道经典题目为什么 s1 s2 是 true而 s1 s3 是 false此时就引入了字符串常量池的概念Strings1hello;Strings2hello;Strings3newString(hello);System.out.println(s1s2);// trueSystem.out.println(s1s3);// false字符串常量池指 JVM 中专门用于存储字符串字面量literal的一块特殊内存区域。从而避免重复创建相同内容的字符串对象节省内存提高性能。字符串常量池的位置现代 JavaJDK 8/11/17/21…里 基本可以按“字符串常量池在堆上”来理解JDK 6字符串常量池常被认为在“永久代”方法区的一种实现里JDK 7 起字符串常量池迁移到堆更便于 GC、避免永久代容易 OOMJDK 8 起永久代移除类元数据转到元空间本地内存但字符串常量池仍在堆这一点延续典型例子常量池中已存在的字符串进行复用Stringaabc;Stringbabc;System.out.println(ab);// true同一份池对象对于String s “abc”;操作会先检查常量池中是否有 “abc”如果没有在常量池中创建该字符串对象并返回引用。如果有直接返回已有引用。Strings1123;Strings2123;Strings3123new String 会创建新变量Stringaabc;StringbnewString(abc);System.out.println(ab);// falseSystem.out.println(a.equals(b));// true对于 String s new String(“abc”);操作来说会在堆中创建一个新的 String 对象无论常量池是否存在。同时如果常量池中没有 “abc”也会在常量池中创建一份。new String(“abc”) 会在堆中创建一个新的 String 对象该对象会复制常量池中或参数中字符串的字符数组因此它与常量池中的字符串对象内容相同但地址不同StringanewString(123);StringbnewString(123);new String(“a” “b”)StringsnewString(ab);new String(“a” “b”) 在编译期会把 “a” “b” 常量折叠成 “ab”等价于new String(“ab”)此时常量池中创建 1 个ab堆上创建 1 个new String(“ab”) 对象。堆中的对象复制常量池中的 “ab” 的字符数组两者地址不同。“a” “b”Stringsab;编译期常量折叠“a” “b” 直接被优化为 “ab”然后 JVM 会从常量池中查找或创建 “ab”并将引用赋给 s。因此 s 直接指向常量池中的 “ab”不会在堆上创建新对象。此时常量池中创建 1 个ab堆上没有创建任何新对象。str1 str2Stringstr1ab;Stringstr2cd;Stringstrstr1str2;变量拼接在编译期无法确定结果JVM 会在运行时通过 StringBuilder 的 append 和 toString 创建新字符串。此时常量池中只有 “ab” 和 “cd”不包含 “abcd”堆上创建 1 个新的 String 对象内容为 “abcd”。str“a”Stringstr1ab;Stringstr2str1cd;str1 是变量“cd” 是字面量编译器无法在编译期确定结果因此会在运行时通过 StringBuilder或 StringBuffer进行拼接。此时常量池中只有 “ab” 和 “cd” 但不会有 “abcd”堆上会创建 str2 指向的新 String 对象内容为 “abcd”new String(“ab”) “ab”;StringsnewString(ab)ab;new String(“ab”) 在堆上创建一个 “ab” 对象复制常量池中的 “ab” 字符数组然后与字面量 “ab” 拼接实际通过 StringBuilder 生成新的字符串。此时常量池中仅存在 “ab”堆上共创建 2 个对象第一个是 new String(“ab”) 产生的对象第二个是拼接后 toString() 产生的新 String 对象内容为 “abab”。new String(“ab”) new String(“ab”)StringsnewString(“ab”)newString(“ab”)两个堆上的 String 对象拼接同样通过 StringBuilder 生成新字符串。此时常量池中存在 “ab”堆上共创建 3 个对象第一个 new String(“ab”)、第二个 new String(“ab”)以及拼接后 toString() 产生的新 String 对象内容 “abab”new String(“ab”) new String(“cd”);StringsnewString(“ab”)newString(“cd”);两个不同内容的堆对象拼接生成新的字符串。常量池中存在 “ab” 和 “cd”堆上共创建 3 个对象new String(“ab”)、new String(“cd”)以及拼接后 toString() 产生的新 String 对象内容 “abcd”字符串拼接的底层原理Java 中字符串拼接的实现经历了优化演变Java 8 及之前编译器将 转换为 StringBuilder 的 append 方法最后调用 toString() 生成新字符串。Java 9 之后引入了 invokedynamic 动态指令和 StringConcatFactory性能更高但本质仍然是在堆上创建新对象。intern( ) 函数intern() 是 String 类的一个本地方法用于手动将字符串对象添加到常量池如果池中还没有相同内容并返回常量池中的引用publicnativeStringintern();如果常量池中已经存在一个字符串通过 equals 比较内容相等则返回池中的引用。如果常量池中不存在则将该字符串对象添加到常量池并返回该对象的引用此时堆对象和池中对象是同一个。典型代码如下StringsnewString(1)newString(1);s.intern();Strings211;System.out.println(ss2);// JDK 7 为 trueJDK 6 为 falseJDK 6intern() 在永久代复制了一份 “11”s 仍指向堆s2 指向永久代所以 false。JDK 7intern() 将堆中 s 对象的引用存入常量池s2 也获取该引用所以 true。

更多文章