Java中String、StringBuffer和StringBuilder

来源:互联网 发布:淘宝炉石金币号怎么买 编辑:程序博客网 时间:2024/05/29 16:25

浅析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


0 0
原创粉丝点击