Bootstrap

[JVM] String#intern 面试必会

String#intern

本文主要通过一个小demo, 说明一下String#intern 的用法, 以及为什么会是这么个执行结果

``

从这个小程序说起

``

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
}

你确定你能说的清为什么是这个结果吗 ?

``

``

先说答案呢 ? 还是先讲源码呢 ? 纠结

尝试分析

String#intern 源码注释

先看下源码怎么说吧, 目的是看看这方法是干嘛的, ``

/**
* Returns a canonical representation for the string object.
* 

* A pool of strings, initially empty, is maintained privately by the * class {@code String}. *

* 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. *

* 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}. *

* All literal strings and string-valued constant expressions are * interned. String literals are defined in section 3.10.5 of the * The Java™ Language Specification. * * @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类维护字符串池 ( 池刚开始为空池 ) 。

``

``

``

因此,对于任何两个字符串s和t,当且仅当s.equals(t)为true时,s.intern()== t.intern()为true。

所有文字字符串和字符串值常量表达式均已插入。字符串文字是在Java™语言规范的3.10.5节中定义的。 返回值: 与该字符串具有相同内容的字符串,但保证来自唯一字符串池。

看了文档注释, 咱们大概了解了String#intern的作用,

从另一个角度, 看下 intern 单词的意思 ?

**``)**

Case 解析

针对 case 挨个分析一波吧

  • case1

众所周知, ``

一个"张三"在字符串常量池中被引用, 另外一个在堆中被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 , ``, 跟普通对象其实没什么区别了, 也会参与GC 内存回收,

就是因为和普通对象属于同一空间了, 所以, 当池中字符串不存在时, 调用String#intern 才有可能返回他自身的引用, 复用了存储空间

而在 JDK 6 的情况下, StringTable 不属于堆空间, 所以不论什么时候调用String#intern方法都是另一个空间的对象, 和当前调用者内存地址肯定不同, 所以在JDK 6 的运行环境下, 咱们的 case3 打印结果会是 false !!!

课后习题

感兴趣的话, 可以在JDK 6 环境下运行看一下区别, 希望本文对你有帮助