不同JDK版本中String intern()方法行为变更原因探究
String.intern()在不同JDK版本的行为差异解析
先看测试代码:
String s1 = new String("1") + new String("1"); s1.intern(); String s2 = "11"; System.out.println(s1 == s2);//jdk6:false;jdk7:true;jdk8:true;jdk17:false
各版本输出差异的原因
JDK6 输出false
- JDK6的字符串常量池位于永久代,和普通对象所在的堆是完全分离的内存区域。
new String("1") + new String("1")会在堆内存中创建一个内容为"11"的新String对象,这个对象不在常量池里。- 调用
s1.intern()时,因为常量池里没有"11",会把堆里的这个"11"对象复制一份到永久代的常量池,返回的是常量池里这个副本的引用。 - 之后
String s2 = "11"是直接引用永久代常量池里的副本,s1指向的是堆里的原对象,两者不是同一个实例,所以==比较结果为false。
JDK7/JDK8 输出true
- 从JDK7开始,字符串常量池被移到了堆内存中,和普通String对象处于同一内存区域。
new String("1") + new String("1")依旧在堆里创建"11"对象。- 调用
s1.intern()时,发现常量池里没有"11",这时候不会复制对象,而是直接把堆中现有"11"对象的引用存入常量池,返回的就是这个引用。 - 之后
String s2 = "11"会去常量池查找,拿到的就是s1指向的那个堆对象的引用,所以s1 == s2结果为true。
JDK17 输出false
- JDK17对字符串常量池的实现做了优化调整,引入了更严格的实例隔离机制。
- 调用
s1.intern()时,即使常量池里没有"11",也不会复用堆中已有的s1对象的引用,而是会创建一个独立的新String实例存入常量池(或者使用JVM内部优化的专属实例)。 String s2 = "11"引用的是常量池里的这个新实例,和s1指向的堆原对象不是同一个,所以==比较结果为false。
intern()方法的版本变更
- JDK6及更早:常量池在永久代,intern()会复制目标字符串对象到常量池,返回常量池中的副本引用。
- JDK7到JDK16:常量池迁移到堆,intern()在常量池无对应字符串时,直接存入堆中现有对象的引用,返回该引用。
- JDK17及以后:intern()不再复用堆中现有对象的引用,常量池中的字符串为独立实例,确保常量池与堆中普通对象的实例隔离。
内容的提问来源于stack exchange,提问作者王睿明
相关产品推荐
相关产品推荐

