写点什么

[JVM] String#intern 面试必会

发布于: 2020 年 05 月 27 日
[JVM] String#intern 面试必会

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&trade; 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 环境下运行看一下区别, 希望本文对你有帮助



发布于: 2020 年 05 月 27 日阅读数: 188
用户头像

为而不争,一切还都不算晚,加油! 2018.09.26 加入

Java 从业者, 目前就职于杭州。 有个公众号(不过还没写东西,哈哈):猴哥一一

评论

发布
暂无评论
[JVM] String#intern 面试必会