面试题
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。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。