字符串在编程中使用的非常频繁,同时又是面试中的常见题型,那么我们的对字符串相关类String,StringBuilder,StringBuffer的理解真的正确吗?今天就通过对三个类源码的阅读,来进一步加强理解。
打开String类的源码,可以发现String类是被final所修饰的,因此String类不可以被继承。
同样的,用来保存值得char数组 value也是被final修饰的,这就可以得出关于String的一个很重要的结论。
String是字符串常量,值是不可改变,通常我们对String的操作都是通过new一个新的String对象来完成的
如下图中的subString方法和replace方法。
既然已经有了String这个功能完备的嘞,那么为什么还需要StringBuilder和StringBuffer呢?
让我们来看一下这两个类的源代码:
可以看出,这两个类共同继承于AbstractStringBuilder,那么打开这个类的源码看一下:
可以看到,这个抽象类中也是以char数组的形式来保存字符串,但是,这个数组是可变的,我们看一下append方法的代码:
append()是最常用的方法,它有很多形式的重载。上面是其中一种,用于追加字符串。如果str是null,则会调用appendNull()方法。这个方法其实是追加了’n’、’u’、’l’、’l’这几个字符。如果不是null,则首先扩容,然后调用String的getChars()方法将str追加到value末尾。最后返回对象本身,所以append()可以连续调用。
那么StringBuffer、StringBuilder的区别在哪里呢?
这是StringBuffer的length方法和capacity方法。
这是AbstractStringBuilder的length方法和capacity方法(Stringbuilder没有进行重写)。
很明显,Stringbuffer对大部分方法添加了 synchronized关键字,来保证线程安全。
从上面的一些源码中可以简单分析出String,StringBuilder,StringBuffer的一些异同点,如下:
说了这么多,在实际应用过程中,到底应该注意点什么呢?
下面来实际测试一下:
public Map<String, Map<String, String>> stringAnalytics() {
Map<String, Map<String, String>> result = new HashMap<>();
Map<String, String> time = new HashMap<>();
long pre = System.currentTimeMillis();
String s = "";
for (int i = 0; i < 50000; i++) {
s += i;
}
time.put("String", String.valueOf(System.currentTimeMillis() - pre));
long preBuilder = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 50000; i++) {
sb.append(i);
}
time.put("StringBuilder", String.valueOf(System.currentTimeMillis() - preBuilder));
long preBuffer = System.currentTimeMillis();
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < 50000; i++) {
stringBuffer.append(i);
}
time.put("StringBuffer", String.valueOf(System.currentTimeMillis() - preBuffer));
result.put("time", time);
System.out.print(result);
return result;
}
在上面的代码,分别使用String,StringBuilder,StringBuffer进行了50000的字符串拼接操作(String使用+方法,其他两个类使用append方法),每次拼接的值为当前循环的数字。在该部分执行前后记录当前系统时间,最后算出消耗时间。
得到的结果如下图:
终于到了喜闻乐见的结论时候:
PS:以下结论使用于大部分情况,实际编译编码过程中会有编译优化等原因稍微影响结论,但不能代表大多数。
1.当字符串改动较小的时候,使用String 原因:方便且线程安全
2.当字符串需要频繁进行改动,且单线程使用StringBuilder 原因:由5中可知,StringBuilder是效率最高的了。
3.当字符串需要频繁改动,且多线程调用。使用StringBuffer 原因:StringBuffer中添加了对多线程应用时的保护,可以保证线程安全,且性能下降并不严重,在可接受范围内。
2018-09-22 完成
以上皆为个人所思所得,如有错误欢迎评论区指正。
欢迎转载,烦请署名并保留原文链接。
联系邮箱:huyanshi2580@gmail.com
更多学习笔记见个人博客——>呼延十
var gitment = new Gitment({ id: 'String Stringbuilder Stringbuffer异同', // 可选。默认为 location.href owner: 'hublanker', repo: 'blog', oauth: { client_id: '2297651c181f632a31db', client_secret: 'a62f60d8da404586acc965a2ba6a6da9f053703b', }, }) gitment.render('container')