浅析Java中的String、StringBuffer和StringBuilder
一、String
String类是final类,即不能被继承,且其成员方法默认为final方法;
String类的方法都不是在原有的字符串上进行操作,而是重新生成一个新的字符串对象,即进行这些操作后原始的字符串没有改变;
二、String、StringBuffer、StringBuilder
1 String str = "hello"; 和 String str = new String("hello"); 的区别
public class Main {
public static void main(String[] args) {
String str1 = "hello world";
String str2 = new String("hello world");
String str3 = "hello world";
String str4 = new String("hello world");
System.out.println(str1==str2); // false
System.out.println(str1==str3); // true
System.out.println(str2==str4); // false
}
}
在JVM内存机制中的class文件中有一部分存储编译期间生成的字面常量和符号引用,称为class文件常量池;在运行期间对应着方法区的运行时常量池;
上述代码中String str1 = "hello world"; 和 String str3 = "hello world"; 都在编译期间生成字面常量和符号引用,在运行期间字面常量""hello world"被存储在运行时常量池(只保存一份);通过该方式将String对象跟引用绑定,JVM执行引擎先在运行时常量池中查找是否存在相同的字面常量,若存在则直接将引用指向已存在的字面常量;否则在运行时常量池开辟一个空间来存储字面常量,并将引用指向该字面常量;
通过new关键字生成的对象是在堆内存进行的,而在堆内存进行对象生成的过程是不会检测该对象是否已存在,因此通过new创建对象一定是不同的对象,即使字符串的内容是相同的;
2 String、StringBuffer、StringBuilder
2.1 String和StringBuffer
public class Main {
public static void main(String[] args) {
String string = "";
for(int i=0;i<10000;i++){
string += "hello";
}
}
}
string += "hello";的过程相当于将原有的string变量指向的对象内容取出与"hello"作字符串相加操作,再存入另一个新的String对象当中,再让string变量指向新生成的对象;
该循环结束后new了10000个对象;
public class Main {
public static void main(String[] args) {
StringBuilder stringBuilder = new StringBuilder();
for(int i=0;i<10000;i++){
stringBuilder.append("hello");
}
}
}
该循环结束后只new了1个对象;
2.2 StringBuffer和StringBuilder
StringBuffer和StringBuilder区别在于StringBuffer类的成员方法前有synchronized关键字,在多线程访问时起到安全保护作用,即StringBuffer是线程安全的;
2.3 使用场景
String、StringBuffer和StringBuilder的执行效率是StringBuffer > StringBuilder > String(相对地);
当字符串相加操作或改动较少时,建议使用String;
当字符串相加操作或改动较多时,建议使用StringBuilder;
若采用多线程,建议使用StringBuffer;
三、常见面试题
1 下面这段代码的输出结果?
String a = "hello2"; String b = "hello" + 2; System.out.println((a == b));
输出为true,因为"hello"+2在编译期间优化成"hello2",因此在运行期间变量a和b指向的是同一对象;
2 下面这段代码的输出结果?
String a = "hello2"; String b = "hello"; String c = b + 2; System.out.println((a == c));
输出为false,因为有符号引用存在,String c = b + 2;不会在编译期间优化,即不会将b + 2当作字面常量处理,这种方式生成的的对象保存在堆上,因此a和c指向不同对象;
3 下面这段代码的输出结果?
String a = "hello2"; final String b = "hello"; String c = b + 2; System.out.println((a == c));
输出为true,因为被final修饰的变量在class文件常量池中保存一个副本,对final变量的访问在编译期间直接替换成真实的值,因此String c = b + 2;在编译期间优化成String c = "hello" + 2;;
4 下面这段代码的输出结果?
public class Main {
public static void main(String[] args) {
String a = "hello2";
final String b = getHello();
String c = b + 2;
System.out.println((a == c));
}
public static String getHello() {
return "hello";
}
}
输出为false,因为虽然b用final修饰,但其赋值是通过方法调用返回的,其值只能在运行期间确定,因此a和c指向不是同一个对象;
5 下面这段代码的输出结果?
public class Main {
public static void main(String[] args) {
String a = "hello";
String b = new String("hello");
String c = new String("hello");
String d = b.intern();
System.out.println(a==b);
System.out.println(b==c);
System.out.println(b==d);
System.out.println(a==d);
}
}
输出为false false false true,因为String类的intern方法是本地方法,在Java SE6之前,intern方法在运行时常量池中查找是否存在内容相同的字符串,若存在则返回指向该字符串的引用,若不存在则将字符串入池,返回一个指向该字符串的引用;
6 String str = new String("abc");涉及几个String对象?
2个,因为在类加载过程中,在运行时常量池中创建一个"abc"对象;在执行过程中,在堆上创建一个"abc"对象;
7 下面这段代码1>和2>的区别?
public class Main {
public static void main(String[] args) {
String str1 = "I";
//str1 += "love"+"java"; 1>
str1 = str1+"love"+"java"; //2>
}
}
1>比2>效率高,因为1>中的"love"+"java"在编译器期间优化成"lovejava",而2>中的不会优化;
在命令行窗口键入"javap -c Main",发现1>中只进行1次append操作,2>中进行2次append操作;
注明:此文章是转载海子的博文,详情见:http://www.cnblogs.com/dolphin0520/p/3778589.html