面试题
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()); }
输入后果
springingspringingtruejavajavafalse
依照惯例来说,不应该两个都是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。