jdk1.6和jdk1.7字符串intern()区别

这篇博客探讨了在Java 1.6和1.7中,字符串intern()方法返回结果不同的原因。在1.6中,如果常量池中没有相同值的字符串,intern()会创建新字符串放入常量池;而在1.7中,如果堆中已有该字符串,intern()会直接引用。通过分析StringBuilder的toString()方法和字符串的延迟加载机制,解释了为何某些情况下intern()返回的引用可能与原始字符串不同。此外,文章还提到了`sun.misc.Version`类在JDK类库初始化时如何影响字符串常量池,并讨论了字符串拼接的最佳实践。
摘要由CSDN通过智能技术生成

经典面试题 (jdk 1.6)

String str1 = new String("11");

System.out.println(str1.intern() == str1); // false

String str = new String("1") + new String("1");

System.out.println(str.intern() == str); // false

jdk 1.7

String str1 = new String("11");

System.out.println(str1.intern() == str1); // false

String str = new String("2") + new String("1");

System.out.println(str.intern() == str); // true

为什么上面两道题,intern()返回的结果不一致?

在jdk1.7以前,字符串常量池存在于方法区中  

此时我们调用intern(),他最终会返回该字符串在常量池中的地址(首先判断常量池中是否存在,如果存在直接返回常量池中字符串的地址,不存在则在常量池中新建该字符串并将新建地址返回)。

在jdk1.7及以后,字符串常量池存在于堆空间内 

此时调用intern()时,他会先查看堆空间中是否存在该字符串,如果存在,常量池直接引用堆空间中该字符串的地址,不存在才会在常量池中新建地址并且返回。

s1 = "a";

s2 = "b";

s3 = "ab";

s4 = s1 + s2;

执行s1 + s2的过程实际操作是new StringBuilder().append("a").append("b").toString()(这属于编译器优化)。StringBuilder中的toString()源码如下,

@Override
public String toString() {
    // Create a copy, don't share the array
    return new String(value, 0, count);
}

因此s1 + s2就相当于new String(“ab”),重新new了一个String实例,是在堆上开辟了一块内存存储这个字符串,因此s3和s4不相等。

当执行到指令ldc #2时,才会把符号a变为“a”字符串对象(这叫字符串的延迟加载)

s3 = "ab";

s5 = "a" + "b";

从反编译结果可以看出,"a" + "b"是直接去StringTable中找字符串“ab”,而不是先找“a”,再找“b”,最后再拼接,可以看到s5变量的取值,和s3变量的取值是一样的过程。因此s3和s5值相等。

s6 = s4.intern();

先去局部变量表中Slot为4位置的数据(“ab”,这个“ab”字符串时堆上的,因为取的是s4的值),然后调用intern()方法尝试将这个字符串放入StringTable中,如果存在,则不放入,直接拿来用,否则放入,并返回StringtTable中的这个对象(关于intern()具体知识看下边的例子)。

上边是JDK7及其之后的情况,JDK6的时候,执行s.intern(),如果常量池中没有和s相同值的字符串,则会重新创建一个字符串,把这个字符串放到常量池中,并没有使用s指向的那块空间,所以常量池中的"ab"和s并不相等。

《深入理解java虚拟机》这本书上出现了同样的题目:

而答案如下:

可是问题又来了,哪儿出现的“java”这个字符串呢?明明我只运行了一个main方法啊?

答案就是:

sun.misc.Version类!

sun.misc.Version 类会在JDK类库的初始化过程中被加载并初始化,而在初始化时它需要对静态常量字段根据指定的常量值(ConstantValue)做默认初始化,此时被 sun.misc.Version.launcher 静态常量字段所引用的"java"字符串字面量就被intern到HotSpot VM的字符串常量池——StringTable里了。

private static final String launcher_name = "java";

private static final String launcher_name = "@@launcher_name@@";

问题五:那么这段代码又创建了几个对象?

String str3 = new String("a") + new String("a");

答案是五个

因为使用+号的String字符串拼接,底层其实都是先创建一个StringBuilder对象,然后调用append方法把要+的字符串都append进去,最后toString创建一个新的String对象如下图:

红色的地方就是new出来对象的语句,而绿色则是两次append

四个红色一共四个对象,再加上字符串常量池上创建的"a"对象,一共五个

这也正是为什么阿里巴巴代码规范中不建议在for循环里使用+号拼接字符串

https://blog.csdn.net/qq_51166736/article/details/126148640

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值