关于String的Java面试题:
1.面试题一:
String s1="abc";
String s2="xyz";
String s3=s1+s2;
String s4="abc"+"xyz";
String s5="abcxyz";
System.out.println(s3==s4);
System.out.println(s4==s5);
JVM内存分析图:
分析:
s1 = “abc”; s1指向存放在常量池中的“abc”。
s2 = “xyz”; s2指向存放在常量池中的”xyz”。
s3 = s1+s2; 因为s1+s2是变量相加,所以s3所指向的是存放在堆中的“abcxyz”;在反编译之后,s3 = new StringBuilder().append(s1).append(s2).toString(),通过StringBuilder中的append()方法将两个变量s1,s2追加到堆中。
s4 = “abc”+”xyz”;s4是通过两个常量值相加,在编译时,已经将值变为“abcxyz”,存放在常量池中。
s5 = “abcxyz”;因为常量池中已存在“abcxyz”,常量池中的字符串可以重用,,所以s5和s4都指向常量池中的abcxyz”;
结论:
s3==s4 显然两者指向的内存地址不同,所以结果为false;
s4==s5 两者都指向常量池的同一个值,所以返回结果为true.
注:只有字面赋值的常量字符在编译时,会存放在“常量池”中;而在运行过程中发现的字符串或者变量拼接得来的值存放在“堆”中。
2.面试题二:
String s1="abc";
String s2="xyz";
String s3=s1+s2;
String s4=s3.intern();
String s5="abcxyz";
System.out.println(s3==s4);
System.out.println(s4==s5);
JVM内存分析图:
分析:
s1 = “abc”; s1指向存放在常量池中的“abc”。
s2 = “xyz”; s2指向存放在常量池中的”xyz”。
s3 = s1+s2; 因为s1+s2是变量相加,所以s3所指向的是存放在堆中的“abcxyz”;
s4 = s3.intern(); 使用intern()方法首先会判断在常量池中有没有这个值,如果没有,则把对象复制一份(或对象引用)放入常量池中,返回常量池中的对象,如果常量池中存在,则直接返回。(注:JDK1.7之前是把值复制一份放入常量池,JDK1.8之后则把对象引用到常量池)。此处s3的值不在常量池中,所以s3.intern()将s3存放在堆中的地址复制放在常量池中,并返回该地址,s4则指向该地址,即堆中s3的值“abcxyz“;
s5 = “abcxyz”; 在常量池中已经存放着指向“abcxyz“,s5发现已存在,所以直接指向堆中的”abcxyz”;
结论:
s3 == s4 ; 由于两者指向同一个块地址,所以返回true;
s4 == s5 ; s5指向的s3的值,s3和s4相同,所以s5和s4也相同,返回true。
3.特殊一点:
String str1="ja";
String str2="va";
String str3=str1+str2;
String str4=str3.intern();
System.out.println(str3==str4);
因为Java的常量池中事先存放了某些常使用的关键字,拼接后的str3的值是存放在堆中,而str4 = str3.intern()会在常量池中寻找是否存在,如果没有才将堆中值或引用复制过来,但是由于Java常量池中事先存放了”java,void,main…”等值,所以不用复制,直接返回常量池中地址。因此str3和str4比较时会返回false.(简单说就是地址不同)。