STRING
Intro(String) #
内存布局 #
intern() #
String.intern()
方法表示获取一个字符串对象的值在常量池中的引用。如果常量池中存在,则直接返回引用。如果不存在,常量池创建并返回引用。
不同jdk版本创建并返回引用是有区别的:
JDK1.6,不存在则在永久代字符串常量池创建等值字符串
,
JDK1.6之后,在堆区字符串常量池创建的是堆中String obj的引用
。 参考1, 参考2
另外字符串常量池的实现是 C++ 中的hashtable。String s = new String("ABC")
这行单纯代码层面来说,只创建一个对象。如果包含类加载的过程,有可能会创建两个,一个在常量池中,一个在堆里面。堆里面的引用常量池中的value。 参考
不要使用JUnit之类的工具测试intern方法,有可能会有误差, 详细解释查看case3测试 #
下面的代码在main方法以及JUnit中运行的效果不同。
解释:
此处的不正常现象在于JUnit会加载一些其他类,这些类里面定义了一些需要加载到字符串常量池(SCP)的字面量。而且JVM的SCP仅有一个,所以出现这种现象。
现象分析:
如果在当前方法执行之前JUnit相关类已经加载"11"
到字SCP。String s3 = new String("1") + new String("1");
在堆中创建了一个新的对象(包括字符串对象中的value引用也是最新的)。但是String s4引用的是SCP中存在的,这两个对象互不相关。【不像main方法执行"s3.intern()“的时候,SCP 里面没对应的值,所以将s3的引用放在SCP里面,定义s4的时候自然将SCP中的那一个s3引用给s4,也就是s4 == s3】 。当然,除了"11"字符串外,还有如下可以验证的值"10","775","813","923","CN","星期日","大正","昭和","巴基斯坦卢比","java.home"
,只需要将上述字符串拆解即可验真。注意:main方法测试的时候根据原理,发现一个
"java.home"
字符串,也会出现不一致的情况。应该还有其他类似的。
Reference #
- https://stackoverflow.com/questions/27812666/why-string-intern-behave-differently-in-oracle-jdk-1-7
- https://stackoverflow.com/questions/19672427/string-s-new-stringxyz-how-many-objects-has-been-made-after-this-line-of
- https://blog.csdn.net/tyyking/article/details/82496901
- https://www.cnblogs.com/mic112/p/15520770.html#字符串常量池
- https://drive.google.com/file/d/1YLB0u2DXSNAccpvdH_EZSwTTnYchHOKs/view?usp=sharing