一、简介
String:不可变字符序列
StringBuffer:线程安全的可变字符序列
StringBuilder:非线程安全的可变字符序列
二、示例分析
【示例1】
String A = "123"; String B = "123"; String C = "456"; String D = new String("123");
String B = "123";先检查“123”在字符串常量池中是否存在,如果存在将B指向已有的“123”,如果不存在(如C),则在常量池中新加入“456”;
String D = new String("123");其中“123”也指向常量池中的“123”;
String A = "123"; String B = "123"; System.out.println(A==B); String C = new String("123"); System.out.println(A==C); String D = new String("123"); System.out.println(C==D);
输出结果:(“==”比较的是地址是否相同)
true false false
【示例2】
String A = "123"; String B = "456"; String AB = A+B; String C = "123456"; System.out.println(AB==C); String D = "123"+"456"; String F = "123456"; System.out.println(D==F);
输出结果:
false true
String AB = A+B;首先会在堆中创建一个StringBuilder对象,并将A作为初始化的值;再通过append()方法合并B的值,最后再通过toString()方法在堆中创建一个String对象,将堆地址存放在AB中。
“123”+“456”;在编译期就会自动合并为“123456”,所以D与F指向的是同一个地址。
【示例3】
//代码1 String A = ""; for(int i=0;i<100000;i++){ A=A+i; } //代码2 StringBuilder B =new StringBuilder(""); for(int i=0;i<100000;i++){ B.append(i); }
代码1耗时:28067ms
代码2耗时:15ms
A=A+i,进行累加时,会出现和示例二中同样的情况,不停的生成对象,导致性能很差。
B.append(i),不会生成新的对象,只是同一个对象的不停累加。
三、总结
1、能够确定的字符串常量,完全没有必要创建StringBuilder或StringBuffer对象。直接使用字符串常量的"+"连接操作效率最高。
2、StringBuilder和StringBuffer对象的append效率要高于String对象的“+”操作。
3、StringBuilder比StringBuffer的性能稍高,但是StringBuffer线程安全。当有多线程的情况出现时,StringBuffer更安全。
解释一下,此处所说的线程安全是指StringBuffer调用append、insert等方法时线程安全。线程安全的原因,是因为这些方法都被关键字synchronized修饰。
关键字“synchronized”是为线程同步机制设定的。
每一个类对象都对应一把锁,当某个线程A调用类对象O中的synchronized方法M时,必须获得对象O的锁才能够执行M方法,否则线程A阻塞。一旦线程A开始执行M方法,将独占对象O的锁。使得其它需要调用O对象的M方法的线程阻塞。只有线程A执行完毕,释放锁后。那些阻塞线程才有机会重新调用M方法。这就是解决线程同步问题的锁机制。