String

字符串常量

-

JDK1.0

StringBuilder

字符串变量

线程不安全

JDK1.0

StringBuffer

字符串变量

线程安全

JDK1.5

三者区别

String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象。如果经常改变字符串内容,最好不要用 String ,因为每次生成对象都会对系统性能产生影响,而且当内存中无引用的对象多了以后, JVM 的 垃圾回收器GC 就会开始工作,程序运行速度就会变慢。如果是定义一个StringBuffer 类型的对象,每次对字符串内容进行操作都会对 StringBuffer对象本身进行操作,而不是生成新的对象。所以一般情况下推荐使用 StringBuffer ,特别是字符串对象经常改变的情况下。StringBuffer中很多方法可以带有synchronized关键字,所以可以保证线程是安全的,但StringBuilder的方法则没有该关键字,所以不能保证线程安全。StringBuilder类提供一个与 StringBuffer 兼容的 API,该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。两者的方法基本相同。



在某些特殊的情况下,String的效率比StringBuffer要快,例如:

String s1 = "123" + "456" + "789";
StringBuffer s2 = new StringBuffer("123").append("456").append("789");

第一行代码在java虚拟机看来其实就是:

String s1 = "123456789";

因为在编译期就已经完成了。


关于字符串==的问题

public static void main(String[] args) {  
		String s1=new String("123");            
		String s2=new String("123");      
		System.out.println("s1==s2 :" + (s1==s2));  // false       
		   
		String s3="456";    
		String s4="456";  
		System.out.println("s3==s4 :" + (s3==s4));  // true 
	}
	
	输出:
	s1==s2 :false
	s3==s4 :true

s1和s2是java虚拟机在堆内存new出来的两个String对象的内存地址,虽然他们的值一样,但是==比较的是地址,所以是false。而s3和s4是在常量池中保存的,就是常量池中“456”指向的唯一的保留字符串对象的地址,所以是true。

public static void main(String[] args) {  
		String s1 = "123";                                          
		String s2 = "456";                                       
		String s12 = s1 + s2;  
		String s="123456";  
		System.out.println(s12==s); // false  
	}

变量s1,s2存储的是常量池中的地址。当执行s1+s2时,JVM首先会在堆中创建一个StringBuilder类,同时用s1指向的字符串对象完成初始化,然后调用append方法完成对s2所指向的字符串进行合并操作,接着调用StringBuilder的toString()方法在堆中创建一个String对象,最后将刚生成的String对象的堆地址存放在变量s12中。而变量s存储的是常量池中"abcd"所对应的字符串对象的地址。s12与s地址肯定不一样。


总结

String:适用于少量的字符串操作的情况

StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况

StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况

在编译期就能够确定的字符串常量,没有必要创建String或StringBuffer对象。直接使用字符串常量的"+"连接操作效率最高。