关于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内存分析图:

android string字符串动态拼接_常量池

分析:

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内存分析图:

android string字符串动态拼接_java string 值_02

分析:

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.(简单说就是地址不同)。