前言
看了一篇文章,大概是讲面试中的java基础的,有如题这么个面试题。我又翻了一些文章看了下,然后去看源码。看一下源码大概能更加了解一些。
String
String类是final的,表示不可被继承,不可变的。注释上也有说明:
其中用一个char[]来保存String的值。
private final char value[];
一旦赋值,就不可改变。看一下,操作string的源码,例如 substring,replace,concat 等方法,最终结果都是返回 new String(...). 也就是重新创建了一个String对象。
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); }
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); }
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 方法。我们看一下代码实现:
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; }
private void ensureCapacityInternal(int minimumCapacity) { // 当append之后的长度大于当前value的长度的时候,需要重新生成一个char[]数组。 if (minimumCapacity - value.length > 0) { value = Arrays.copyOf(value, newCapacity(minimumCapacity)); } }
public static char[] copyOf(char[] original, int newLength) {
//重新创建一个char[]数组 char[] copy = new char[newLength];
//将之前的值赋值到该数组中 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非线程安全。可以根据他们不同的特性分情况使用。