四行代码的 Java Puzzle----老司机快来刷题解惑

2017-09-25 11:27:34 +08:00
 ZiLong
        String str1 = new StringBuilder("hel").append("lo").toString();
        String str2 = new StringBuilder("ja").append("va").toString();
        System.out.println(str1.intern() == str1); // true
        System.out.println(str2.intern() == str2); // false

百思不得琦姐,为什么这个输出一个是 true,一个是 false ?

3905 次点击
所在节点    Java
16 条回复
zxyroy
2017-09-25 11:33:14 +08:00
貌似和 string 长度有关,具体的我一时间查不到
rozbo
2017-09-25 11:44:19 +08:00
很简单因为`java`这个字符串之前被初始化过。
而另外一个没有。。
hcymk2
2017-09-25 11:57:19 +08:00
运行环境也不说个。
yankebupt
2017-09-25 11:59:43 +08:00
猜测有可能是因为"java"因为常用所以池中已有,append 出来的那个并不是第一个生成的所以 intern 不能指向自身...
据说不是纯引号的 literal 字符串用==会比较 reference 而不是 intern 的值,要比较值需要用.equals....
是这样么.
CallFold
2017-09-25 12:00:26 +08:00
常量池
lzx801
2017-09-25 12:01:19 +08:00
When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.
ZiLong
2017-09-25 12:01:37 +08:00
@rozbo `java` 是被加载到 String 的字符串常量池了。但是 toString 不是返回一个新的 String 对象么?怎么会相等
yinheli
2017-09-25 12:18:24 +08:00
推荐书籍: 《深入理解:java 虚拟机》

intern 不同的虚拟机可能实现不同,HotSpot ( oracle jdk,openjdk )中不同的版本也有差别。
你应该用的是 jdk 7 及以上版本。

jdk7 开始,不再复制 string 实例到永久代,只是在常量池记录首次出现的实例引用,它返回的是首次创建实例的引用。

java 这个字符串比较特殊,你没写但是其他的地方也用到过。它已经在常量池(引用)里了。
adslxyz
2017-09-25 12:51:20 +08:00
其实这段代码的结果在不同的虚拟机实现上可能是不同的。

比如在 Oracle JDK8 中,是 true,false.

而在 OpenJDK8 中,就是 true,true.

具体原因可以参看 Oracle JDK8 的代码:

路径:./jdk/src/share/classes/sun/misc/Version.java.template

其中:

```
private static final String launcher_name =
"@@launcher_name@@";
```

这个 @@launcher_name@@是模版占位符,会被替换为配置文件里指定的值。

如果是 Oracle JDK8,LAUNCHER_NAME=java

如果是 OpenJDK8 的话,LAUNCHER_NAME=openjdk

所以 Oracle JDK8 在初始化 sun.misc.Version 类的时候会将"java"给 intern。

同理,OpenJDK8 则会将"openjdk"给 intern。
SuperMild
2017-09-25 13:23:56 +08:00
google java string intern,前面几个连接就把这个问题说得不能更清楚了,基础技术问题为什么不 google 呢。
ZiLong
2017-09-25 15:09:22 +08:00
@lzx801 我其实在问之前反复看了这段话,然而我满脑子里反应的是字符串常量池是把它拷贝过去的。。。
不过看了 @yinheli 的回答才知道原来 JDK6 还真这么做的
ZiLong
2017-09-25 15:09:56 +08:00
@yinheli
@adslxyz
感谢两位!
ZiLong
2017-09-25 15:11:06 +08:00
@adslxyz 请问大神是怎么在这么多类里找到`java`这个字符串是出现在哪个类呢
shard
2017-09-25 16:49:33 +08:00
@ZiLong #13
我来教你一个方法:
dump 你的堆;
下载 Eclipse Memory Analyzer ;
导入你 dump 文件;
然后 oql 搜索 select * from java.lang.String s where s.toString() = "java",在结果中 list incoming object
大概这样。
hubert3
2017-09-25 16:55:19 +08:00
@yinheli 是不是不太准确?java7 的常量池也在永久代里面,java8 开始才没有永久代
ZiLong
2017-09-25 23:12:58 +08:00
@hubert3 虽然永久代是 Java8 移除的,但是在 Java 7 中,常量池就被移出了永久代,参见官方说明: [http://www.oracle.com/technetwork/java/javase/jdk7-relnotes-418459.html]( http://www.oracle.com/technetwork/java/javase/jdk7-relnotes-418459.html)
```
Area: HotSpot
Synopsis: In JDK 7, interned strings are no longer allocated in the permanent generation of the Java heap, but are instead allocated in the main part of the Java heap (known as the young and old generations), along with the other objects created by the application. This change will result in more data residing in the main Java heap, and less data in the permanent generation, and thus may require heap sizes to be adjusted. Most applications will see only relatively small differences in heap usage due to this change, but larger applications that load many classes or make heavy use of the String.intern() method will see more significant differences.
RFE: 6962931
```

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/393395

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX