String#intern
本文主要通过一个小demo, 说明一下String#intern 的用法, 以及为什么会是这么个执行结果
生活很苦, 时常我们会迷惘, 而我们要主动去寻找希望, 加油
从这个小程序说起
文中小程序运行环境为 JDK 8
public static void main(String[] args) {
// case1
String origin1 = new String("张三");
System.out.println(origin1.intern() == origin1); // false
// case2
String origin2 = new StringBuilder().append("aaa").append("bbb").toString();
String s1 = "aaabbb";
System.out.println(origin2.intern() == origin2); // false
// case3
String origin3 = new StringBuilder().append("ccc").append("ddd").toString();
System.out.println(origin3.intern() == origin3); // true
}
你确定你能说的清为什么是这个结果吗 ?
注意看清题目, 谁和谁比较, 不要看错了, 别自以为是
其实case2 case3 初看还是挺有迷惑性的 !
先说答案呢 ? 还是先讲源码呢 ? 纠结
尝试分析
String#intern 源码注释
先看下源码怎么说吧, 目的是看看这方法是干嘛的, 分析程序, 看源码准没错
/**
* Returns a canonical representation for the string object.
* <p>
* A pool of strings, initially empty, is maintained privately by the
* class {@code String}.
* <p>
* When the intern method is invoked, if the pool already contains a
* string equal to this {@code String} object as determined by
* the {@link #equals(Object)} method, then the string from the pool is
* returned. Otherwise, this {@code String} object is added to the
* pool and a reference to this {@code String} object is returned.
* <p>
* It follows that for any two strings {@code s} and {@code t},
* {@code s.intern() == t.intern()} is {@code true}
* if and only if {@code s.equals(t)} is {@code true}.
* <p>
* All literal strings and string-valued constant expressions are
* interned. String literals are defined in section 3.10.5 of the
* <cite>The Java™ Language Specification</cite>.
*
* @return a string that has the same contents as this string, but is
* guaranteed to be from a pool of unique strings.
*/
public native String intern();
大概翻译一下:
该方法用于返回字符串对象的规范表示。 由String类维护字符串池 ( 池刚开始为空池 ) 。
调用intern方法时,
如果池中已经包含与此String对象equals()方法相同的字符串,则返回池中的字符串引用。
否则,将此String对象添加到池中,并返回对此String对象的引用。
因此,对于任何两个字符串s和t,当且仅当s.equals(t)为true时,s.intern()== t.intern()为true。
所有文字字符串和字符串值常量表达式均已插入。字符串文字是在Java™语言规范的3.10.5节中定义的。 返回值: 与该字符串具有相同内容的字符串,但保证来自唯一字符串池。
看了文档注释, 咱们大概了解了String#intern的作用,
从另一个角度, 看下 intern 单词的意思 ?
动词, 拘留
, 也能看出其实是想把字符串关押起来(往常量池中添加对象
)
Case 解析
针对 case 挨个分析一波吧
-
case1
众所周知,
String origin1 = new String("张三"); 实际会产生两个字符串对象,
一个"张三"在字符串常量池中被引用, 另外一个在堆中被orgin1 引用,
因为池中已有"张三", origin1.intern() 返回的是常量池中的对象引用, 所以和 origin1 地址不相同
-
case2
String origin2 = new StringBuilder().append(“aaa”).append(“bbb”).toString();
这一步实际上会产生几个字符串对象呢?
常量池中会引用 “aaa” “bbb” 两个,
看下 StringBuilder#toString 源码, 实际上会返回一个 new String(value, 0, count);
String s1 = “aaabbb”;
这一步 会往常量池中添加一个 “aaabbb” 对象
所以, origin2.intern() 返回的是 常量池中的地址, 和 origin2 地址不同
-
case3
case3 与 case2 只是少了 String s1 = “cccddd”;
也就是说 常量池中不存在 “cccddd” 字符串, 那么 origin3.intern(), 执行结果会把字符串本身添加到常量池, 并返回自身引用, 所以这一步 origin3.intern() == origin3 是true
干货赠送: JVM背景知识
Hotspot VM , JDK 7(包括) 以后
, 字符串常量池也就是 StringTable, 就从永久代移动到堆空间了
, 跟普通对象其实没什么区别了, 也会参与GC 内存回收,
就是因为和普通对象属于同一空间了, 所以, 当池中字符串不存在时, 调用String#intern 才有可能返回他自身的引用, 复用了存储空间
而在 JDK 6 的情况下, StringTable 不属于堆空间, 所以不论什么时候调用String#intern方法都是另一个空间的对象, 和当前调用者内存地址肯定不同, 所以在JDK 6 的运行环境下, 咱们的 case3 打印结果会是 false !!!
课后习题
感兴趣的话, 可以在JDK 6 环境下运行看一下区别, 希望本文对你有帮助