《你并不了解 String》 勘误

简介: 《你并不了解 String》 勘误

写技术文章其实是个很好的学习方式。首先你得自己摸清楚原理,然后才能尝试去表达出来。你写出来的东西,别人看了,给予你反馈,也是一个互相学习的过程。这不,前几天碰到一个读者提出的一个问题,让我发现了自己文章中的一个疏漏,下面就来说说这个问题。


在我之前的一篇文章 你并不了解 String 中出了这样一道题目:

String str1 = new String("j") + new String("ava"); // 1
str1.intern(); // 2
String str2 = "java"; // 3
System.out.println(str1 == str2); // 4
复制代码


看一遍,想一遍,如果还不能准确给出答案的话可以再仔细看看我的那篇文章。正常情况下,答案应该是 true。贴一下之前的讲解:

经过编译,j 、ava 和 java 进入 Class 常量池 中。 类加载阶段并不会创建实例,驻留字符串常量池。到运行期,第一行代码中会创建 j 、ava 实例并驻留常量池,+ 会被 JVM 自动优化为 StringBuilder ,拼接出 java 字符串,将 str1 指向该字符串实例。需要注意的是,这里不会将 java 驻留到常量池。第二行代码调用了 intern(),由于此时常量池中没有 java,所以将 str1 的引用存入了常量池。第三行代码,ldc 指令发现常量池中就有 java,直接返回常量池中其对应的引用,并赋给 str2。所以 str1 和 str2 是相等的。


看起来说的还是挺有道理的,我自己放到 IDEA 中执行也的确是 true。但是有一位读者给我评论说他执行下来是 false。我的第一反应就是 “我的代码肯定没错,应该是他写错了” ,就加了这兄弟的微信,截图给我看了看代码。然后我就蒙了,果然是 false。同样的代码,却是不同的执行结果。


那么,究竟 str1str2 为什么会不相等呢?

我们再来分析一下代码,第一行:

String str1 = new String("j") + new String("ava"); // 1
复制代码


从这句话中可以肯定的是,str1 指向堆中的一个 java 字符串实例,且这个字符串是在堆中新创建的。再看第二和第三行:

str1.intern(); // 2
String str2 = "java"; // 3
复制代码


str1.intern()str1 是指向堆中的一个 java 字符串实例的,调用 intern() 的话,此时就有两种情况:

  1. 字符串常量池中没有 java 字符串的引用
  2. 字符串常量池中已经有了 java 字符串的引用


第一种情况,就是我上篇文章中的分析,str1 驻留到字符串常量池,结果是 true。第二种情况,字符串常量池中已经有了 java,此时再执行 str1.intern() 就会直接返回字符串常量池中 java 字符串对应的引用,并不会将 str1 驻留到字符串常量池。


String str2 = "java"; 一执行,str2 等于字符串常量池中的 java 对应的引用。而 str1 是新建在堆中的 java 字符串的引用,自然而然,比较结果是 false

这么分析下来,这位读者无疑是第二种情况了。但是为什么同样在 main() 方法中直接执行这几行代码,结果会不一样呢?为什么 main() 方法中的代码还没有执行,字符串常量池中就已经有 java 字符串的引用了呢?


对啊,为什么会这样呢?等等,java,这个字符串是不是有点特殊。我尝试着让这位读者换一个特殊点的字符串再运行一次,竟然还让我蒙对了,这下打印 true 了。看到这里,你应该明白了,在 JVM 启动的过程中,字符串常量池已经在发挥作用了,在 main() 方法运行之前,一些字符串引用已经驻留在字符串常量池,比如上面的 java,但也不是百分之百的,我手里的 Ubuntu 18.04 就一直打印的是 true,你们也可以掏出电脑来试一试。


对于 String.intern() 方法,我们只需要搞清楚当前字符串常量池是否已经驻留该字符串引用,已驻留和未驻留将导致不同的执行逻辑。

  • 已驻留,直接返回字符串常量池中的引用
  • 未驻留,将当前字符串引用驻留进字符串常量池并返回该引用


这么一来,我和这位读者都彻彻底底的搞清楚了 intern() 方法,下次再遇到类似的面试题应该都不是问题了。


相关文章
每日一道面试题之String常用的方法有哪些?
每日一道面试题之String常用的方法有哪些?
|
1月前
|
C语言 C++
深度剖析C++string(中)
深度剖析C++string(中)
47 0
|
6月前
|
C++
c++的学习之路:11、string(3)
c++的学习之路:11、string(3)
34 0
|
6月前
|
编译器 C++
c++的学习之路:10、string(2)
c++的学习之路:10、string(2)
44 0
|
6月前
|
移动开发 索引 Python
|
Java C# C++
34.【C/C++ string大汇总,不看定后悔】(一)
34.【C/C++ string大汇总,不看定后悔】
53 0
34.【C/C++ string大汇总,不看定后悔】(一)
|
C++
34.【C/C++ string大汇总,不看定后悔】(二)
34.【C/C++ string大汇总,不看定后悔】
49 0
|
API C语言 C++
【C++】对于string的学习
【C++】对于string的学习
48 0
|
编译器 C++
c++学习之string实现
c++学习之string实现
64 0
|
存储 缓存 安全
第十章、String字符串
在C语言中要表示字符串只能使用字符数组或者字符指针,可以使用标准库提供的字符串系列函数完成大部分操作,但是这种将数据和操作数据方法分离开的方式不符合面向对象的思想,而字符串应用又非常广泛,因此Java语言专门提供了String类。在开发和校招笔试中,字符串也是常客。面试中也频繁被问到,比如:String、StringBuff和StringBulider之间的区别等。由于String的不可更改特性,为了方便字符串的修改,Java中又提供StringBuilder和StringBuffer类。
52 0