最近在看java基础知识时,看到一些有关String的面试题,发现有许多有关string的知识点理解不够透彻。在此结合一些常见面试题做一下记录
创建几个对象
实例代码:
public static void testString1(){
//此句代码会产生两个对象,一个在编译类加载(解析)阶段产生,一个在运行时产生。
String s1= new String("weiwei");
//________________________________________
//先在常量池创建对象
String s2 = "zhang";
//创建一个对象
String s3 = new String("zhang");
//________________________________________
//创建一个对象
String s4 = "a"+"b"+"c";
//________________________________________
String s5 = "helloworld";
final String s6 = "hello";
//没有创建对象
String s7 = s6+"world";
//________________________________________
String s8 = "how are you";
String s9 = "how are";
//创建一个对象
String s10 = s7+"you";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
说明:
加载执行s1语句的时候,会产生两个对象,第一个对象是在类加载的解析阶段,会将字符串“weiwei”在常量池中创建(首先在常量池查找,此处假设其他地方的代码中没有此字符串常量),第二个对象时在代码运行期间,在堆上创建内容为“weiwei”的对象。
执行s2,s3语句,首先s2语句,在加载阶段和s1语句加载相同,常量池不存在,会在常量池中创建对象。s3语句,在类加载解析阶段,由于s2语句已经确保在常量池中存在“zhang”对象,故不会产生对象,在运行期间,new关键字,会在堆上产生一个对象。故s3语句只会生成一个对象。
执行s4语句,首先java文件编译的时候,会将s4语句优化为 String s4 = “abc”;故s4语句,会在类的加载解析阶段,创建一个“abc”对象存放在常量池中,在运行期间,不会创建新的对象,而是将常量池“abc”的引用,赋值给s4。
执行 s5,s6,s7语句,首先s5语句,同s4语句相同,会在常量池中产生一个对象。s6,s7语句在编译期间会进行优化,s6直接优化成字符串字面常量,s7会优化成 s7=”hello”+”world”也即是 s7=”helloworld”;s7语句不会新建对象。
执行s8,s9,s10,s8和s9同s2语句执行逻辑相同。s10在编译期间,会将+号编译成 StringBuild.append(),在装载阶段“you”会在字符串常量池产生一个对象; 由于StringBuild会产生一个对象,故s10语句会产生两个对象。
对应String对象内存图:
对象是否相等
String s1 = "abc";
String s2 = "abc";
System.out.println(s1 == s2);
System.out.println(s1.equals(s2));
1
2
3
4
输出结果:true,true。
说明:s1和s2都指向字符串常量池中的“abc”对象,故s1与s2地址相同(==比较地址是否相同,比较是否为同一个对象)。字符串重写了equals方法,同一个对象返回true,内容相同,也返回true。
String s3= new String("abc");
String s4 = "abc";
System.out.println(s3 == s4);
System.out.println(s3.equals(s4));
1
2
3
4
输出结果:false,true。
说明:s3指向堆中的“abc”对象。s4指向常量池中的对象。s3与s4不指向同一个对象,故第一个返回false。s3与s4内容相同,输出结果true。
String s5 = "a"+"b"+"c";
String s6="abc";
System.out.println(s5== s6);
System.out.println(s5.equals(s6));
1
2
3
4
输出结果:true,true。
说明:s5在编译期间会优化为 s5=”abc”。此时和上面的s1和s2代码逻辑一样。输出结果和s1 s2相同。
String s7 = "ab";
String s8 = "abc";
String s9 = s7+"c";
System.out.println(s9 == s8);
System.out.println(s9.equals(s8));
1
2
3
4
5
6
输出结果:false,true。
说明:s8指向字符串常量池中的“abc”对象,s9语句会在编译期间,优化为stringBuild(s7).append(“c”)。也就是s9最终指向stringBuild.toString()方法返回的“abc”对象(在堆上)。故s9!=s8;内容相同,所以equals返回true。
String s10 = "wenwei";
final String s11 = "wen";
String s12 = s11+"wei";
System.out.println(s10==s12);
1
2
3
4
输出结果:true。
说明:s10指向常量池中的“wenwei”对象。由于s11字符串被final修饰,也就是不可改变常量,s12语句在编译的时候,会优化成s12=”wen”+”wei”。此时s10和s12和s5 s6指向逻辑相同。
String s13 = new String("zhang");
s13.intern();
String s12 = "zhang";
System.out.println(s13 == s12);
1
2
3
4
输出结果:false。
说明:intern(),方法在jdk1.7中执行逻辑是:先检查常量池是否有改字符串对象;如果没有则将s13在堆中的对象引用,赋值给常量池中的变量,并返回本引用,如果存在则直接返回引用。s13代码会在类加载解析阶段,在常量池中生成“zhang”对象,s13.intern()方法,检查常量池存在,不会将s13堆中的对象引用赋值到常量池中;s12语句,指向的是常量池中的对象,s13指向堆中新建的对象。故输出结果为false。
String s14 = new String("1") + new String("1");
s14.intern();
String s15 = "11";
System.out.println(s14 == s15);
1
2
3
4
输出结果:true。
说明:s14语句会在堆中产生一个“11”对象,s14.intern()语句,会将对堆中的“11”对象引用记录的常量池中(jdk1.7以后)。s15语句指向后,s15的值即为常量池中引用的值,也指向堆中的“11”对象。故输输出值为true。
代码优劣
//代码片段1
String result1="";
for(int i =0;i<100;i++){
result1+=i;
}
System.out.println(result1);
//代码片段2
StringBuilder sb = new StringBuilder();
for(int i =0;i<100;i++){
sb.append(i);
}
System.out.println(sb.toString());
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
说明:代码片段1中的result+=i语句编译器编译会优化为,reuslt = new StringBuilder(result).append(i).toString()。这样片段1代码执行100次循环,会创建100个StringBuilder对象,和100个result对象(toString()方法)。代码片段2执行100次循环,就创建了1个StringBuilder和1个String对象。故代码片段2内存占用更少,性能最佳。注意,在java中字符串的+操作,如果连接的不是常量字符串或final修饰的字符变量,编译后都会编译成StringBuilder().append()的形式。
希望对您有所帮助!
————————————————
版权声明:本文为CSDN博主「GeeK_1024Wei」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_22494029/article/details/79306182