从源码看String,StringBuffer,StringBuilder的区别
前言
看了一篇文章,大概是讲面试中的java基础的,有如题这么个面试题。我又翻了一些文章看了下,然后去看源码。看一下源码大概能更加了解一些。
String
String类是final的,表示不可被继承,不可变的。注释上也有说明:
其中用一个char[]来保存String的值。
1 | private final char value[]; |
一旦赋值,就不可改变。看一下,操作string的源码,例如 substring,replace,concat 等方法,最终结果都是返回 new String(...). 也就是重新创建了一个String对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public String substring( int beginIndex, int endIndex) { if (beginIndex < 0 ) { throw new StringIndexOutOfBoundsException(beginIndex); } if (endIndex > value.length) { throw new StringIndexOutOfBoundsException(endIndex); } int subLen = endIndex - beginIndex; if (subLen < 0 ) { throw new StringIndexOutOfBoundsException(subLen); } return ((beginIndex == 0 ) && (endIndex == value.length)) ? this : new String(value, beginIndex, subLen); } |
1 2 3 4 5 6 7 8 9 10 | public String concat(String str) { int otherLen = str.length(); if (otherLen == 0 ) { return this ; } int len = value.length; char buf[] = Arrays.copyOf(value, len + otherLen); str.getChars(buf, len); return new String(buf, true ); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | public String replace( char oldChar, char newChar) { if (oldChar != newChar) { int len = value.length; int i = - 1 ; char [] val = value; /* avoid getfield opcode */ while (++i < len) { if (val[i] == oldChar) { break ; } } if (i < len) { char buf[] = new char [len]; for ( int j = 0 ; j < i; j++) { buf[j] = val[j]; } while (i < len) { char c = val[i]; buf[i] = (c == oldChar) ? newChar : c; i++; } return new String(buf, true ); } } return this ; } |
StringBuffer 和 StringBuilder
它两个都是继承自 AbstractStringBuilder 抽象类,不同的是StringBuffer类都是带有 synchronized 关键字的。也就是说,StringBuffer是线程安全的,而 StringBuilder不是。他们中的方法基本上调用 super的方法。也就是AbstractStringBuilder 里实现的方法。
平时用的比较多的就是 append 方法。我们看一下代码实现:
1 2 3 4 5 6 7 8 9 | public AbstractStringBuilder append(String str) { if (str == null ) return appendNull(); int len = str.length(); ensureCapacityInternal(count + len); str.getChars( 0 , len, value, count); count += len; return this ; } |
1 2 3 4 5 6 7 | private void ensureCapacityInternal( int minimumCapacity) { // 当append之后的长度大于当前value的长度的时候,需要重新生成一个char[]数组。 if (minimumCapacity - value.length > 0 ) { value = Arrays.copyOf(value, newCapacity(minimumCapacity)); } } |
1 2 3 4 5 6 | public static char [] copyOf( char [] original, int newLength) {<br> //重新创建一个char[]数组 char [] copy = new char [newLength];<br> //将之前的值赋值到该数组中 System.arraycopy(original, 0 , copy, 0 , Math.min(original.length, newLength)); return copy; } |
ensureCapacityInternal 方法中,将 value的值重新扩展比如 str.append("abc")。假如之前value的值为:{'h','e','l','l','o'},当调用 str.getChars方法之后,会将 abc 追加到value数组后,value值就变成 :{'h','e','l','l','o','a','b','c'}
其实 getChars 方法只是做了一件事情 :System.arraycopy。
如图:
执行完arraycopy之后
总结
String 不可变,StringBuffer和StringBuilder可变,但是StringBuilder非线程安全。可以根据他们不同的特性分情况使用。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?