最近看到这种类型的面试题,十分不解,就对此做了一些研究,现记录一下学习成果,待自己以后回顾查阅:
首选确定一下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.
*/
以上是源码中的注解,我简单解释一下,intern的方法返回字符串对象的规范表示形式。其中它做的事情是:首先去判断该字符串是否在常量池中存在,如果存在返回常量池中的字符串(为了好理解,我觉得可以认为是常量池中的地址),如果在常量池中不存在,先在常量池中添加该字符串,然后返回在堆中的引用地址,jdk1.6和jdk1.7中该方法的功能是一致的,不同的是常量池位置的改变(jdk1.7将常量池放在了堆空间中),下面具体说明。
第一种情况:
String s1 = new String("1");
s1.intern();
String s2 = "1";
System.out.println(s1 == s2);
jdk1.6和jdk1.7的运行结果均为false
这种情况中第一行代码做的事情是:在栈中开辟空间存储s1(指向堆中引用地址)、在常量池中添加"1"、在堆中开辟空间存储对象引用;第二行的s1.intern(),首先去常量池查找,发现有该常量,则返回常量池中的字符串(注意这里并没有使用该返回值),第三行中,在栈中开辟空间存储s2,s2发现常量池中已经有了"1",则直接指向该常量,所以s1和s2一定不等。
第二种情况:
String s1 = new String("1");
System.out.println(s1.intern() == s1);
jdk1.6和jdk1.7的运行结果均为false
这种情况与第一种情况的区别在于是拿s1.intern()的返回值与s1比较,由上述可知s1.intern()返回的是常量池中的字符串,s1指向的是堆中的引用地址,所以二者不想等。
第三种情况:
String s1 = new String("1") + new String("1");
s1.intern();
String s2 = "11";
System.out.println(s1 == s2);
jdk1.6运行结果是false,jdk1.7运行结果是true
第一行代码做的事情(只说对我们有影响的):在栈中开辟空间存储s1(指向堆中引用地址)、在堆中开辟空间存储对象引用("11"),接下来是jdk1.6和jdk1.7之间的区别:
jdk1.6中,s1.intern()运行时,首先去常量池查找,发现没有该常量,则在常量池中开辟空间存储"11",返回常量池中的值(注意这里也没有使用该返回值),第三行中,s2则使栈中空间直接指向常量池,所以s1和s2不相等。
jdk1.7中,由于常量池在堆空间中,所以在s1.intern()运行时,发现常量池没有常量,则添加常量并使其指向堆空间地址,返回堆空间地址(注意这里也没有使用该返回值),这时s2通过查找常量池中的常量,同样也指向了堆空间地址,所以s1和s2相等。
第四种情况:
String s1 = new String("1") + new String("1");
System.out.println(s1.intern() == s1);
jdk1.6运行结果是false,jdk1.7运行结果是true,接下来我们分别说明流程
这种情况与第三种情况的区别在于是拿s1.intern()的返回值与s1比较
jdk1.6中,常量池在Perm区中,s1.intern()去常量池中查找"11",发现没有该常量,则在常量池中开辟空间存储"11",返回常量池中的值,s1指向堆空间地址,所以二者不相等。
jdk1.7中,常量池在堆空间,s1.intern()去常量池中查找"11",发现没有该常量,则在常量池中开辟空间,指向堆空间地址,则返回常量池指向的堆空间地址,s1也是堆空间地址,所以二者相等。
总结
这种类型题主要考察的知识点有:
1、intern(本地化)的动作:常量池中存在和常量池中不存在
2、jdk1.6和jdk1.7中常量池的位置
3、intern的返回值是什么
4、比较对象是哪些
感谢@我想静静525的指出,我又重新审视了自己的思路和说辞,第四种方法的确是错了,完全和上面反了,以后还是要仔细些啊!