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对象。直接使用字符串常量的"+"连接操作效率最高。