我有一个特别好的朋友,就叫小烦吧,今天请假出去面试,然后碰了一鼻子灰,回来之后死皮赖脸的让我把这些内容记录然后分享出来,我拗不过它,只好答应下来。
事情的经过是这样的。
我那个最好的朋友小烦给经理请假,理由是嘴角一个口疮长在了痔上,没办法说话要在家静养一天。经理虽然满头问号,但还是批准了。
然后小烦就去了某某跳动去面试。
刚一坐下,对面的面试官脑门一闪,把电脑扭到小烦面前:“这段代码的执行结果是什么?”
小烦定睛一看:
String str1 = new StringBuilder().append("ja").append("va").toString();
System.out.println(str1.intern() == str1);
String str2 = new StringBuilder().append("Solid").append("Snaker").toString();
System.out.println(str2.intern() == str2);
这种难度的小问题还能难住我?然后信心百倍的给出答案:
都是true!
面试官摇了摇头,点了run();
false
true
小烦一脸懵逼:what?这俩有啥不一样吗?
面试继续进行,小烦满脑子都是问号,心思没放在面试上,自然答得一塌糊涂。
随着面试官的一句“出门左转是电梯”,这次面试也划上了尾声。小烦二话没说冲回家,打开电脑面向百度编程一顿搜索,然后又查阅了相关书籍,终于找到了原因所在。
“java”这个字符串有问题!
首先我们来看一眼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.
* @jls 3.10.5 String Literals
*/
public native String intern();
主要注意这一段:
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.
翻译过来的意思是:
当intern方法被调用时,如果常量池中已经包含了一个字符串,该字符串等于这个对象,那么池中的字符串将被返回。否则,该对象将被添加到池中,并返回对该对象的引用。
其实,intern()是一个Native方法,底层调用C++的 StringTable::intern方法实现。当通过语句str.intern()调用intern()方法后,JVM 就会在当前类的常量池中查找是否存在与str等值的String,若存在则直接返回常量池中相应Strnig的引用;若不存在,则会在常量池中创建一个等值的String,然后返回这个String在常量池中的引用。因此,只要是等值的String对象,使用intern()方法返回的都是常量池中同一个String引用,所以,这些等值的String对象通过intern()后使用==是可以匹配的。
其实严格来说,上面那段代码在JDK1.6中,会得到两个false;而在JDK1.7及之后中运行,则会得到一个false和一个true。
《深入理解java虚拟机》这本书上出现了同样的题目:
而答案如下:
可是问题又来了,哪儿出现的“java”这个字符串呢?明明我只运行了一个main方法啊?
答案就是:
sun.misc.Version类!
sun.misc.Version 类会在JDK类库的初始化过程中被加载并初始化,而在初始化时它需要对静态常量字段根据指定的常量值(ConstantValue)做默认初始化,此时被 sun.misc.Version.launcher 静态常量字段所引用的"java"字符串字面量就被intern到HotSpot VM的字符串常量池——StringTable里了。
private static final String launcher_name = "java";
private static final String launcher_name = "@@launcher_name@@";
感兴趣的同学可以自己去看看OpenJDK,看一下这个类里面的东西。
这下答案水落石出了,小烦高兴的瞬间啃了两个香辣猪肘子~~~
然后去了肛肠科。
第二天去到公司,经理看到小烦捂着屁股站不起来的样子,脑海里的问号又多了几个。
因为你会,所以你不会。
我是Solid-Snaker,我们下期再见。