Java中String、StringBuffer、StringBuilder知识要点讲解,
Java中String、StringBuffer、StringBuilder知识要点讲解,
String 字符串常量 StringBuffer 字符串变量(线程安全)StringBuilder 字符串变量(非线程安全)
String
Strings are constant; their values cannot be changed after they are created.
这是JavaAPI中对于String的定义,就是说String是常量,一旦定义就无法改变。说到常量就不得不提到Java中的字符串常量池,通过String的俩种创建方法来进行了解
String俩种创建方式区别
Java中字符串对象创建有两种形式,一种为字面量形式,如String str = “abc”;,另一种就是使用new这种标准的构造对象的方法,如String str = new String(“abc”);
当Java中出现第一种字面量形式创建字符串时,JVM首先会在常量池中进行查找是否存在相同字符串对象内容的引用,如果存在,那么直接将新创建的对象指向该引用地址,如果不存在则创建新的引用对象,并把该引用对象放入常量池
public class Test { public static void main(String[] args) { String str1 = "abc"; String str2 = "abc"; System.out.println(str1 == str2); // true } }
通过以上代码可以看到结果为true 也就是说str1 str2 指向的是相同的地址。现在将代码改变:
public class Test { public static void main(String[] args) { String str1 = "abc"; str1 = "xyz"; String str2 = "abc"; System.out.println(str1 == str2); // false } }
当JVM看到"xyz",在字符串常量池创建新的String对象存储它,再把新建的String对象的引用返回给str1,所以这时候str1 指向的是xyz ,而abc 还是存在于字符串常量池中,其值并不能被改变,所以返回false
接下来继续将代码改为以下:
public class Test { public static void main(String[] args) { String str1 = "abc"; str1 = "xyz"; String str2 = "abc"; System.out.println(str1 == str2); // false String str3 = new String("abc"); System.out.println(str2 == str3); // false } }
通过new 来创建一个新的String对象str3 ,这个过程是首先,JVM在常量池中找是否存在abc ,如果找到,不做任何事情,如果没找到,在常量池中创建该对象;接着由于有new 关键字,会在栈内存空间(不是字符串常量池)中创建str3 对象,并指向堆内存空间(不是字符串常量池)中的abc ,接下来进行str2 == str3 比较的时候肯定不是引用的同一个对象,所以返回false
所以说使用new创建String对象过程中是产生了俩个对象
另外Java中还提供了如何将new创建的对象手动放入常量池,看以下代码:
public class Test { public static void main(String[] args) { String str1 = "abc"; str1 = "xyz"; String str2 = "abc"; System.out.println(str1 == str2); // false String str3 = new String("abc"); System.out.println(str2 == str3); // false String str4 = str3.intern(); System.out.println(str2 == str4); // true } }
这样结果就返回的是true
StringBuilder VS StringBuffer
通过上面的介绍我们简单的了解了String是不可变的,是一个常量,因此当我们使用String来构建一个动态的字符串对于内存的开销是特别大的,因为对于每个字符串我们都得分配新的内存空间
所以在Java开发初就提供了StringBuffer JavaAPI对于StringBuffer 的说明是
A thread-safe, mutable sequence of characters.
这个可变的类来创建动态的字符数组,而这个只需要分配一次内存,这与String相比是节省了大量的内存空间,另外,由于其是线程安全的,所以其所有的public 方法都是synchronized ,而这样的设计就会导致,当开发人员是在单线程的环境中时,就会增加额外的开销,而且大多数对于StringBuffer 的使用基本上都是在单线程的环境中,所以开发人员根本不需要对于synchronized 的额外开销,所以在jdk1.5之后提供一个新的类StringBuilder ,这个类不是线程安全的也不是同步的,但是其使用更加方便而且在单线程环境中更加快速
性能测试
下面通过代码进行测试当创建动态的字符数组时String、StringBuffer、StringBuilder 三者需要的时间对比,测试代码如下:
public class Test { public static void main(String[] args) { new Test().constructDynamicString(); } public void constructDynamicString() { long startTime = System.currentTimeMillis(); String s = "test"; //StringBuffer s = new StringBuffer("test"); //StringBuilder s = new StringBuilder("test"); for(int i=0; i<100000; i++) { s += "concat"; //s.append("concat"); } long endTime = System.currentTimeMillis(); System.out.println("Concat time ====== " + (endTime - startTime) + "ms"); } }
结果如下:
String创建时间: 43144ms StringBuffer创建时间: 14ms StringBuilder创建时间: 14ms
第一次测试结果都是14ms,应该是数字较小,循环1000000次,统计结果
StringBuffer: Concat time ====== 80ms StringBuilder: Concat time ====== 43ms
通过以上测试可以看出,在创建动态字符串过程中,使用String需要的时间最久,而使用StringBuilder时间最短,而且创建的次数越多这个性能越明显。
总结
通过以上的分析,那么在实际使用中我们怎么选择呢?
1、如果创建静态的字符串而且在程序中是不变的,那么选择String
2、如果是创建动态字符串,并且是在多线程中使用,那么用StringBuffer
3、其他的都使用StringBuilder,并且大多数情况下都是使用StringBuilder
用户点评