[JVM] String#intern 笔试分享

本文通过三个案例详细解析了Java中String#intern方法的工作原理,解释了为何在不同情况下其行为有所不同,尤其是在JDK8环境下的表现。文章还探讨了JVM背景知识,对比了JDK6与JDK7之后字符串常量池的变化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值