- 聊聊String、StringBuilder以及StringBu
- String StringBuilder 以及 StringBu
- String、StringBuffer、StringBuilde
- StringBuffer和StringBuilder的区别
- StringBuilder与StringBuffer的区别
- 简单讲讲String、StringBuffer和StringBu
- Java中的String,StringBuilder,Strin
- 11,String,StringBuilder,StringBu
- 十一、String、StringBuilder、StringBu
- 测试String, StringBuilder,StringBu
特征
String:字符串常量;
StringBuilder:字符串变量(非线程安全);
StringBuffer:字符串变量(线程安全);
![](https://img.haomeiwen.com/i2993097/3a9f420de30eb44c.png)
可以看出,三者都实现了CharSequence接口,并且StringBuilder和StringBuffer都继承了AbstractStringBuilder类。通过阅读源码还能得知,三者内部都是通过一个char[ ]数组来实现功能的。
异同点:
- 都被final声明了,不可被继承;
- String不可变,每次使用都新建一个对象。而StringBuffer和StringBuilder是可变的;
- 在字符串不经常变化的背景下使用String,在对频繁对字符串进行运算的背景(如拼接、修改、删除等)下使用StringBuffer或StringBuilder。
![](https://img.haomeiwen.com/i2993097/38f9d4233e46af11.png)
String
对于String来说,有两种创建对象的方式:
- 使用字面量形式,例如 String a = "EakonZhao";
- 使用构造函数的方式,例如String b = new String("EakonZhao");
关于创建String对象的一些知识,例如字面量常量池(也称字符串常量池)等在我这篇博客有提到。
聊聊Java中的 " == "、equals以及hashCode
让我们来看看下面这个程序:
![](https://img.haomeiwen.com/i2993097/7bb7859ea851e7de.png)
我们马上可以说出打印的结果:EakonZhao。我们也很容易误认为我们是将字符串"Eakon"与“Zhao”进行拼接然后得到“EakonZhao”,并且此时"EakonZhao"将原有的“Eakon”替换掉了。
事实真的如我们想象的那样吗?
如果真的是这样理解,那我们就错了。别忘了String是不可变的,创建之后就不能修改了。
其实看完下面这个示意图就很容易理解了:
![](https://img.haomeiwen.com/i2993097/058b6004deee3590.png)
其实不管是原有的“Eakon”还有“Zhao”或者是后来生成的"EakonZhao",其实都是存放在不同的内存空间的。
也就是说,name最初引用的是值为“Eakon”的字符串对象,至于最终打印出的结果是“EakonZhao”,并不是说对name引用的对象的内容进行了替换,而是将name的引用指向了一个新的对象---值为"EakonZhao"的字符串对象。
那么在使用 “ + ”对字符串进行连接的时候,底层发生了什么事情呢?
下面我将借助jad反编译工具来查看字节码的内容:
![](https://img.haomeiwen.com/i2993097/be007fefc571e01b.png)
第32行,将字符串“Eakon”压入栈中
第35行,创建了一个StringBuilder对象
第39行,由于使用了+,所以调用了StringBuilder对象的append方法,参数是后面的"Zhao"
第40行,将字符串“Zhao”压入栈中
第41行,使用append方法连接“Eakon”和"Zhao"
第42行,调用toString方法
所以我们可以知道,当我们使用 “ + ” 对字符串进行操作时,其实底层会转换成调用StringBuilder的append方法来实现。
其实如果用加号对String对象进行连接的话,效率是十分低下的,并且每次都要创建一个新的对象,也会占用大量的系统资源。
下面我将对String、StringBuffer以及StringBuilder的性能进行探究:
分别使用String、StringBuffer、StringBuilder进行150000次的字符串拼接操作,然后获得执行时间,取六次执行时间求得平均值来比较性能。
public class Eakon{
private int LOOP_TIMES = 150000;
private final String TEST_STRING = "EakonZhao";
public void testString(){
String eakon = "";
long beginTime = System.currentTimeMillis();
for(int i = 0; i < LOOP_TIMES; i++){
eakon += TEST_STRING;
}
long endTime = System.currentTimeMillis();
System.out.print(endTime-beginTime+" ");
}
public void testStringBuffer(){
StringBuffer eakon = new StringBuffer();
long beginTime = System.currentTimeMillis();
for(int i = 0 ; i < LOOP_TIMES; i++){
eakon.append(TEST_STRING);
}
eakon.toString();
long endTime = System.currentTimeMillis();
System.out.print(endTime-beginTime+" ");
}
public void testStringBuilder(){
StringBuilder eakon = new StringBuilder();
long beginTime = System.currentTimeMillis();
for(int i = 0; i < LOOP_TIMES; i++){
eakon.append(TEST_STRING);
}
eakon.toString();
long endTime = System.currentTimeMillis();
System.out.print(endTime-beginTime+" ");
}
public void run(){
for(int i = 0; i < 5; i++){
System.out.println("第" +i+ "次:");
testStringBuilder();
testStringBuffer();
testString();
System.out.println("----------------------------------");
}
}
public static void main(String[] args){
new Eakon().run();
}
}
测试结果:
![](https://img.haomeiwen.com/i2993097/5a3789bb1d882135.png)
![](https://img.haomeiwen.com/i2993097/a159a42ffc2fb9f9.png)
我们可以看出,对String使用“+”操作耗时是最多,性能最差的。StringBuilder比StringBuffer性能略好一些,但我们要注意的是StringBuilder是非线程安全的,也就是StringBuilder牺牲了线程安全性来提升性能。
为什么对String使用“+”操作耗时要那么久呢?因为每使用一次“+”操作,都要新创建一个对象。然而创建的对象马上又会失去引用,从而被垃圾回收机制清理掉。下面我们来看看在使用“+”对字符串进行连接时内存的使用情况:
![](https://img.haomeiwen.com/i2993097/63e0bca8ce1f535d.png)
我们可以看到占用了极大部分的堆内存-----因为对象是存放在堆上的,如果一直不停地创建对象,将会堆内存使用量将会极大地增加。
以上就是我对String、StringBuilder以及StringBuffer简单的介绍,以后我会另开博客来深入探究它们的源码,以便帮助我们更加合理地使用它们。
网友评论