String、StringBuilder、StringBuffer 的理解 浅析我对 String、StringBuilder、StringBuffer 的理解
谨丰 人气:0StringBuilder、StringBuffer 和 String 一样,都是用于存储字符串的。
1、那既然有了 String ,为什么还需要他们两个呢?
原因是 String 是不可变的,它每次的字符串拼接,实际上都会 new 一个新的 String 进行接收。
2、谈谈StringBuilder、StringBuffer他们两个的联系:
我们可以知道 StringBuffer 在 1.0 的时候就发布了,那为什么还需要 StringBuilder 呢?原因是它的大部分方法都上了锁,是线程安全的,导致了效率较低!而我们有时候不需要考虑线程安全问题,追求效率!所以 StringBuilder 在 1.5 的时候就出来了!
3、StringBuilder、StringBuffer 的异同:*
不同:
- StringBuffer 它因为追求安全,给大量方法上锁,线程安全!
- StringBuilder 它因为追求效率,没有给方法上锁,线程不安全!
相同:内部方法和 StringBuffer 完全一致,因为都继承了 AbstractStringBuilder,底层数组都是用父类的。
4、源码浅析 String:
结论:final 修饰了底层的字符数组,故内容不可变。
5、源码浅析 StringBuilder:构造方法
观察构造方法:
public StringBuilder() { super(16); } public StringBuilder(int capacity) { super(capacity); } public StringBuilder(String str) { super(str.length() + 16); append(str); } public StringBuilder(CharSequence seq) { this(seq.length() + 16); append(seq); }
结论:可以看出,它有一个默认的长度 16!而当传入参数是一个字符或者字符串时,它也会自动的传入参数的长度上加上 16!
6、源码浅析 StringBuilder:append 方法
@Override public StringBuilder append(Object obj) { return append(String.valueOf(obj)); } @Override public StringBuilder append(String str) { super.append(str); return this; }
我们发现,它还是调用的父类的 append 方法,说明这个方法他并没有重写,那么 StringBuffer 也一样!
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; }
结论:我们可以看出,他也是可以拼接 null 的!
private AbstractStringBuilder appendNull() { int c = count; ensureCapacityInternal(c + 4); final char[] value = this.value; value[c++] = 'n'; value[c++] = 'u'; value[c++] = 'l'; value[c++] = 'l'; count = c; return this; }
然后观察,它接着进行了一个数组容量的判断,而数组的扩容,其实就是在里面实现的,我们点进去看一下!
private void ensureCapacityInternal(int minimumCapacity) { // overflow-conscious code if (minimumCapacity - value.length > 0) { value = Arrays.copyOf(value, newCapacity(minimumCapacity)); } }
结论:它先是判断,当前数组容量+拼接字符 是否大于 数组长度,如果大于,则进行数组拷贝,并将底层数组的引用指向新数组!
private int newCapacity(int minCapacity) { // overflow-conscious code int newCapacity = (value.length << 1) + 2; if (newCapacity - minCapacity < 0) { newCapacity = minCapacity; } return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0) ? hugeCapacity(minCapacity) : newCapacity; }
结论:由此可见,新数组长度扩容为原数组的 2倍+2 !
问题:那它究竟是怎么拼接字符串的呢?
sb.getChars(0, len, value, count);
进去看一下:String 的 getChars 方法
public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) { if (srcBegin < 0) throw new StringIndexOutOfBoundsException(srcBegin); if ((srcEnd < 0) || (srcEnd > count)) throw new StringIndexOutOfBoundsException(srcEnd); if (srcBegin > srcEnd) throw new StringIndexOutOfBoundsException("srcBegin > srcEnd"); System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin); }
实际调用了一个系统类方法:arraycopy,再点进去看一下!
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
结论:底层最终是调用的本地方法,实现了的字符数组拷贝,但由于本地方法是可以和操作系统直接打交道的,所以它的 append 字符串拼接效率会高于 String!
总结
加载全部内容