JVM中的常量池是一个非常重要的概念主要分为三类Class文件常量池、运行时常量池和字符串常量池。一、Class文件常量池存储内容每个Java类的.class文件中都有一个常量池表主要存储字面量文本字符串、final常量值、基本数据类型的值符号引用类和接口的全限定名、字段的名称和描述符、方法的名称和描述符特点编译时生成存储在每个.class文件中节省内存避免重复创建相同的数据为动态链接提供基础二、运行时常量池特点类加载后Class文件常量池中的内容会进入方法区的运行时常量池具有动态性运行时也可以将新的常量放入池中如String.intern()每个类或接口都有独立的运行时常量池转换过程// 编译时Class文件常量池中的符号引用 // 类加载时Class文件常量池加载到运行时常量池 // 运行时先经过运行时常量池最终在字符串常量池中创建或复用 String s hello; // hello字面量最终进入字符串常量池堆中三、字符串常量池核心特性位置演变JDK 6及之前永久代中JDK 7及之后堆内存中存储内容String对象的引用或直接对象具体取决于JDK版本重要方法intern()String s1 hello; // 直接使用字面量从常量池获取 String s2 new String(hello); // 创建新对象 String s3 s2.intern(); // 将s2指向的字符串内容放入常量池 System.out.println(s1 s2); // false System.out.println(s1 s3); // true内存分布示例// 情况1字面量创建 String a abc; String b abc; System.out.println(a b); // true指向常量池同一个引用 // 情况2new创建 String c new String(abc); System.out.println(a c); // false堆中不同对象 // 情况3intern()方法 String d c.intern(); System.out.println(a d); // true四、不同JDK版本差异JDK 6运行时常量池在永久代字符串常量池在永久代易发生OOM永久代内存溢出JDK 7/8运行时常量池在元空间JDK8或永久代JDK7字符串常量池移到堆中避免了永久代的内存限制// JDK6 vs JDK7 的区别 String s1 new String(a) new String(b); s1.intern(); String s2 ab; System.out.println(s1 s2); // JDK6: false (intern复制到永久代) // JDK7: true (intern存储引用)五、常见面试题1. 以下代码创建几个对象String s new String(abc); 答案 1个或2个对象 堆中1个new String对象 常量池中如果没有abc则再创建1个2. 字符串拼接的优化String s1 a b; // 编译优化为ab String s2 ab; System.out.println(s1 s2); // true String a a; String b b; String s3 a b; // 通过StringBuilder创建新对象 System.out.println(s2 s3); // false3. 基本类型包装类常量池// Integer有缓存池-128~127 Integer i1 127; Integer i2 127; System.out.println(i1 i2); // true Integer i3 128; Integer i4 128; System.out.println(i3 i4); // false // 类似地Byte、Short、Long、Character都有缓存池