在开发过程中,我们最常用的就是对字符串进行操作了,在Java中,与字符串相关的类有这么3个:String、StringBuffer、StringBuilder,下面就来介绍下这三个类的区别。
String
JDK的解释是 “Strings are constant; their values cannot be changed after they are created”也就是说String对象一旦被创建就是固定不变的了,这样的一点好处就是可以多线程之间访问,因为只读不写。Strings类代表字符串。Java 程序中的所有字符串字面值(如 "abc")都作为此类的实例实现。
字符串是常量;它们的值在创建之后不能更改。字符串缓冲区支持可变的字符串。因为 String 对象是不可变的,所以可以共享
一般情况下我们以下面两种方式创建一个String对象。
两种方式是有区别的,这和java的内存管理有关,前面已经说过,string创建之后是不可变的,所以按照第一种方式创建的字符串常量池中,常量池就是用来保存在编译阶段确定好了大小的数据,一般我们定义的int等基本数据类型就保存在这里。其具体的一个流程就是,编译器首先检查常量池,看看有没有一个“string”,如果没有则创建。如果有的话,则直接把str1指向那个位置。
第二种创建字符串的方法是通过new关键字,还是java的内存分配,java会将new的对象放在堆中,这一部分对象是在运行时创建的对象。所以我们每一次new的时候,都会创建不同的对象,即便是堆中已经有了一个一模一样的。
下面举一个例子:
String str1 = "string"; String str4 = "string"; String str2 = new String("string"); String str3 = new String("string"); /*用于测试两种创建字符串方式的区别*/ System.out.println(str1 == str4); System.out.println(str2 == str3); System.out.println(str3 == str1); str3 = str3.intern(); //一个不常见的方法 System.out.println(str3 == str1);
这个的运行结果是
true //解释:两个字符串的内容完全相同,因而指向常量池中的同一个区域
false //解释:每一次new都会创建一个新的对象
false // 解释: 注意==比较的是地址,不仅仅是内容
true //介绍一下intern方法,这个方法会返回一个字符串在常量池中的一个地址,如果常量池中有与str3内容相同的string则返回那个地址,如果没有,则在常量池中创建一个string后再返回。实际上,str3现在指向了str1的地址。
字符串的比较不能直接使用==,因为==比较的是地址,应该使用String类提供的equals()方法。
在使用java的过程中,经常看到的一种写法是
String str1 = "hello";
String str = str1 + "world";
这样是在常量区新建了一个"hello","world"以及一个"hello world",然后将"hello world"的地址赋值给str。所以如果需要对一个字符串不断的修改的话,效率是非常的低的,因为堆的好处是可以动态的增加空间,劣势就是分配新的空间消耗是很大的,比如我们看下面的测试。
public class Main { public static void main(String[] args) { long start = System.currentTimeMillis(); String str = ""; for (int i = 0; i < 50000; i++) { str += " "; } long end = System.currentTimeMillis(); System.out.println("the run time is " + (end - start) + " ms"); } }
运行的结果为:the run time is 809 ms。
StringBuffer
StringBuffer是一个线程安全的,就是多线程访问的可靠保证,最重要的是他是可变的,也就是说我们要操作一个经常变化的字符串,可以使用这个类,基本的方法就是append和insert方法,StringBuffer对象的创建只能使用new而不能使用StringBuffer sb = "";这种形式,也不能使用sb += "";而需要调用append去添加到尾部。
还是上一个例子:
public class Main { public static void main(String[] args) { long start = System.currentTimeMillis(); StringBuffer sb = new StringBuffer(); for (int i = 0; i < 50000; i++) { sb.append(" "); } long end = System.currentTimeMillis(); System.out.println("the run time is " + (end - start) + " ms"); } }
运行结果为:the run time is 11 ms.
StringBuilder
前面说StringBuffer是线程同步的,那么很多情况下,我们只是使用一个线程,那个同步势必带来一个效率的问题,StringBuilder就是StringBuffer的非线程同步的版本,二者的方法差不多,只是一个线程安全(适用于多线程)一个没有线程安全(适用于单线程)。
其实看了一下jdk源代码就会发现,StringBuffer就是在各个方法上加上了关键字syncronized。
参考链接:leeon