共计 1599 个字符,预计需要花费 4 分钟才能阅读完成。
面试题
public static void main(String[] args) {String str1 = new StringBuilder().append("spring").append("ing").toString();
System.out.println(str1);
System.out.println(str1.intern());
System.out.println(str1 == str1.intern());
System.out.println();
String str2 = new StringBuilder().append("ja").append("va").toString();
System.out.println(str2);
System.out.println(str2.intern());
System.out.println(str2 == str2.intern());
}
输入后果
springing
springing
true
java
java
false
依照惯例来说,不应该两个都是 true 吗,为什么下面一个是 true,上面一个是 false 呢?上面咱们来解析这个考题中的考点。
这个面试题中应用了 String 的 intern()办法,这是一个本地办法,它的作用是如果字符串中曾经蕴含一个等于此 String 对象的字符串,则返回代表池中这个字符串的 String 对象援用,否则,会将此 String 对象蕴含的字符串增加到常量池中,并且返回这个 String 对象在堆中的援用。
所以第一个 str1 调用 inter()办法之后返回的还是 str1 在堆中的援用,和 str1 雷同,返回 true。
而第二个字符串 str2(java)这个字符串阐明 str2 调用 intern()这个办法之后返回的不是 java 这个字符串在堆中的援用,返回的是字符串常量池中的 java 字符串援用,然而咱们之前并没有调用 intern()这个办法,为什么在字符串常量池中会存在 java 这个字符串呢?
咱们来看 System 这个类
System 这个类中有一段正文,意思是VM 将调用 initializeSystemClass 办法来实现与 clinit 离开的此类的初始化。请留神,要应用 VM 设置的属性,请参阅 initializeSystemClass 办法中形容的束缚。
所以会通过 initializeSystemClass 这个办法对 VM 进行一些属性设置
咱们再去看 initializeSystemClass
这个办法中 sun.misc.Version.init(); 这部分很重要
咱们再进去
咱们发现,在这个 Version 类中,定义了一个 static final 润饰的 String 类型的 launcher_name,这个字符串内容就是 ”java”。
此外,咱们发现这个 sun.misc.Version
位于 rt.jar 包中,而 rt.jar 这个包是根加载器启动的时候加载的根底类包。
java 类加载器分为根加载器 (也叫启动类加载),扩大类加载器,零碎类加载器和自定义加载器。
java 程序启动的时候不是一次把所有的类全副加载后再运行,它会把保障程序运行的根底类一次性加载到 JVM 中,其余的类期待 JVM 应用到的时候再加载,这些根底类就在 rt.jar 包中。
至此,这个问题咱们就分明了,JVM 启动的时候会加载 tr.jar 这个包中的根底类,其中就包含 sun.misc.Version 这个类,而 sun.misc.Version 这个类中定义了一个 static final 润饰的 java 字符串被放入了字符串常量池中,而咱们在里面定义 java 字符串之后再调用 intern()办法放入字符串常量池的时候,实际上字符串常量池中曾经有了 java 这个字符串,所以 intern()放回的是在启动类加载器加载根底类过程中 sun.misc.Version 这个类中定义的 java。也就导致了最初后果为 false。