举例说明string类和stringbuffer类的区别_String、StringBuffer和StringBuilder的区别

0. 说明

今天看到了一个老的面试题就是:String、StringBuffer和StringBuilder之间的区别。

这个面试题非常经典,其实现在大多数情况下都会考虑到性能为题,在我们写一个类的toString方法的时候,一般都是使用的IDE自动生成,无论生成的代码是"+"链接符,还是其他很么样式,一般情况下都会被IDE在编译的时候,变成性能相对最优的StringBuilder的append的形式(一般情况下,当然IDE具体操作可以自己定制)。

1. StringBuffer和StringBuilder

这两个类我们先从源码中分析下:

public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence{ private transient char[] toStringCache; ... @Override public synchronized int length() { return count; } @Override public synchronized int capacity() { return value.length; } @Override public synchronized void ensureCapacity(int minimumCapacity) { super.ensureCapacity(minimumCapacity); } @Override public synchronized void trimToSize() { super.trimToSize(); } @Override public synchronized void setLength(int newLength) { toStringCache = null; super.setLength(newLength); } @Override public synchronized char charAt(int index) { if ((index < 0) || (index >= count)) throw new StringIndexOutOfBoundsException(index); return value[index]; } @Override public synchronized int codePointAt(int index) { return super.codePointAt(index); } @Override public synchronized int codePointBefore(int index) { return super.codePointBefore(index); } @Override public synchronized int codePointCount(int beginIndex, int endIndex) { return super.codePointCount(beginIndex, endIndex); } @Override public synchronized int offsetByCodePoints(int index, int codePointOffset) { return super.offsetByCodePoints(index, codePointOffset); } @Override public synchronized void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin){ super.getChars(srcBegin, srcEnd, dst, dstBegin); } @Override public synchronized void setCharAt(int index, char ch) { if ((index < 0) || (index >= count)) throw new StringIndexOutOfBoundsException(index); toStringCache = null; value[index] = ch; } @Override public synchronized StringBuffer append(Object obj) { toStringCache = null; super.append(String.valueOf(obj)); return this; } @Override public synchronized StringBuffer append(String str) { toStringCache = null; super.append(str); return this; } public synchronized StringBuffer append(StringBuffer sb) { toStringCache = null; super.append(sb); return this; } @Override synchronized StringBuffer append(AbstractStringBuilder asb) { toStringCache = null; super.append(asb); return this; } @Override public synchronized StringBuffer append(CharSequence s) { toStringCache = null; super.append(s); return this; } @Override public synchronized StringBuffer append(CharSequence s, int start, int end){ toStringCache = null; super.append(s, start, end); return this; } @Override public synchronized StringBuffer append(char[] str) { toStringCache = null; super.append(str); return this; } @Override public synchronized StringBuffer append(char[] str, int offset, int len) { toStringCache = null; super.append(str, offset, len); return this; } @Override public synchronized StringBuffer append(boolean b) { toStringCache = null; super.append(b); return this; } @Override public synchronized StringBuffer append(char c) { toStringCache = null; super.append(c); return this; } @Override public synchronized StringBuffer append(int i) { toStringCache = null; super.append(i); return this; } @Override public synchronized StringBuffer appendCodePoint(int codePoint) { toStringCache = null; super.appendCodePoint(codePoint); return this; } @Override public synchronized StringBuffer append(long lng) { toStringCache = null; super.append(lng); return this; } @Override public synchronized StringBuffer append(float f) { toStringCache = null; super.append(f); return this; } @Override public synchronized StringBuffer append(double d) { toStringCache = null; super.append(d); return this; } @Override public synchronized StringBuffer delete(int start, int end) { toStringCache = null; super.delete(start, end); return this; } @Override public synchronized StringBuffer deleteCharAt(int index) { toStringCache = null; super.deleteCharAt(index); return this; } @Override public synchronized StringBuffer replace(int start, int end, String str) { toStringCache = null; super.replace(start, end, str); return this; } @Override public synchronized String substring(int start) { return substring(start, count); } @Override public synchronized CharSequence subSequence(int start, int end) { return super.substring(start, end); } @Override public synchronized String substring(int start, int end) { return super.substring(start, end); } @Override public synchronized StringBuffer insert(int index, char[] str, int offset, int len){ toStringCache = null; super.insert(index, str, offset, len); return this; } @Override public synchronized StringBuffer insert(int offset, Object obj) { toStringCache = null; super.insert(offset, String.valueOf(obj)); return this; } @Override public synchronized StringBuffer insert(int offset, String str) { toStringCache = null; super.insert(offset, str); return this; } @Override public synchronized StringBuffer insert(int offset, char[] str) { toStringCache = null; super.insert(offset, str); return this; } @Override public StringBuffer insert(int dstOffset, CharSequence s) { super.insert(dstOffset, s); return this; } @Override public synchronized StringBuffer insert(int dstOffset, CharSequence s, int start, int end){ toStringCache = null; super.insert(dstOffset, s, start, end); return this; } @Override public StringBuffer insert(int offset, boolean b) { super.insert(offset, b); return this; } @Override public synchronized StringBuffer insert(int offset, char c) { toStringCache = null; super.insert(offset, c); return this; } @Override public StringBuffer insert(int offset, int i) { super.insert(offset, i); return this; } @Override public StringBuffer insert(int offset, long l) { super.insert(offset, l); return this; } @Override public StringBuffer insert(int offset, float f) { super.insert(offset, f); return this; } @Override public StringBuffer insert(int offset, double d) { super.insert(offset, d); return this; } @Override public int indexOf(String str) { return super.indexOf(str); } @Override public synchronized int indexOf(String str, int fromIndex) { return super.indexOf(str, fromIndex); } @Override public int lastIndexOf(String str) { return lastIndexOf(str, count); } @Override public synchronized int lastIndexOf(String str, int fromIndex) { return super.lastIndexOf(str, fromIndex); } @Override public synchronized StringBuffer reverse() { toStringCache = null; super.reverse(); return this; } @Override public synchronized String toString() { if (toStringCache == null) { toStringCache = Arrays.copyOfRange(value, 0, count); } return new String(toStringCache, true); }...}

题外话:看看这源码,实在是规范,当一行长度达到一定数量的时候,就折行,这样阅读起来很方便,可读性非常强,笔者是真的被同事的超级长的一行代码给弄的怀疑人生过。

再看看StringBuilder的源码

public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence{...// 具体源码请各位自己查看}

从源码中可知StringBuffer和StringBuilder都继承了AbstractStringBuilder抽象类,看下AbstractStringBuilder抽象类的源码:

abstract class AbstractStringBuilder  implements Appendable, CharSequence { char[] value; int count;...}

从上面的源码中可知两个实现类都继承AbstractStringBuilder类,也就继承了value的属性,可知其内部存储数据是以数组的形式存储的。

至于两个类的区别,从源码中也可以很容易的看出来,那就是StringBuffer大部分方法都有synchronized关键字的修饰,从而保证是线程安全的,而StringBuilder是不安全的。

2. String和StringBuilder

StringBuffer和StringBuilder在前文分析中可知,基本上是一样的,唯一的区别就是是否是线程安全的,所以本文在讲String与前两者的区别的时候,我们只讲String和StringBuilder的区别,因为String和StringBuffer的区别与其类似。

具体源码请各位移步到自己电脑的IDE中查看,他们具有的方法都不相同了,但是功能基本上想起,先说下String,String有一个特性就是是一个不可变类,说不可变并不是说不能为一个String类型的变量重新赋,只是说重新赋值的时候,换了对应,也就是String变量指向了新的String对象,而老的对象可能被回收掉。我们看图分析下。对于一个初始为"test"的字符串,在连接另外一个"test"之后对象发生的变化。

24b760a95fb051d1d26389ee7ec73515.png

String和StringBuilder

从图中可以看到当原test字符串cat上新的test变为testtest之后的变化,String方式是新建一个String对象重新指向新的testtest,并将str指向新的String 对象,而StringBuilder并不是这样的,StringBuilder对象并没有新建,而是StringBuilder指向的数组发生了变化,这里笔者画的是新建一个字符数组,其实是一种特殊的情况,这里边也有类似集合扩容的概念,就是初始的时候字符数组有一定的长度,源码中默认的是16。而后在append的时候,如果数组能够满足append之后的存储,那么就不会新建字符数组,否则扩容新建字符数组。所以StringBuilder在实际操作中减少了很多新建对象的操作,也一定量的减少了GC的操作,性能比较String有很多提升。各位看客可以在IDE中DEBUG下看看对象的对象是不是引用发生了变化。我下面把我写的test代码贴出来:

@Testpublic void test_string() { String str = new String("test"); System.out.println(str); str = str + "test"; System.out.println(str); StringBuilder sb = new StringBuilder("test"); System.out.println(sb.toString()); sb.append("test"); System.out.println(sb);}

笔者在IDE中操作的时候,第4行和第6行的str内部value的对象不是同一个,而第9行和第11行的sb的value是同一个。

3. 总结

String、StringBuffer和StringBuilder好像是一个绕不过去的面试话题,本文主要介绍了他们之间的区别,因为他们的功能基本上都很相似。区别表现在性能和线程安全上,如果希望是线程安全的就是用StringBuffer,如果是希望高性能那就选择StringBuilder,如果要是String不可变,就使用String(外加final修饰,因为其他的前两个sb,即便是final修饰,其仍然可以append,会改变真实的String值)。

希望本文对各位看官理解和选择String、StringBuffer和StringBuilder时有所帮助。

求**评论、点赞、关注+转发**

限于笔者知识有限,如果不足之处请帮忙指正,不喜勿喷!

您的支持是我不懈努力的动力,请读者多支持下!

更多文章,请关注微信公众号 CS_Toper之路,或者头条号 CSToper

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值