JAVA中String是不可变的,存放在字符串常量池中。当我们创建一个String时,它就已经是不可改变的了。
观察String源码可以知道String类封装的是char数组常量,因此长度、内容均不可变:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
那么String API中subString、replace返回的是什么呢?
substring:
public String substring(int beginIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
int subLen = value.length - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}
replace:
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;
}
String的API中不论是截取还是替换等操作都是返回一个新的String对象(JVM中会现在字符串常量池中寻找是否含有目的字符串)。
那么如果我们想动态改变String呢?如果使用传统的“+“或者是String API每次生成一个新的String对象,有时会浪费空间。
StrigBuilder 和StringBuffer便是动态改变字符串的类。
StringBuffer是StringBuiler类的多线程版本,两个类的API相同。StringBuffer类中含有对同步锁synchronized的判定,是线程安全的。所以StringBuilder用于单线程,StringBuffer用于多线程。这里重点介绍StringBuilder。
(1)StringBuilder的用法:
首先,构建一个空的字符串构造器:
StringBuilder builder =new StringBuilder();//(调用了无参构造器)
当每次需要添加一部分内容时,就调用append方法。
builder.append(“H”);
builder.append(“E”);
builder.append(“LLO”);
在需要构建字符串时就调用toString方法,将可以得到一个String对象,其中包含了构建器中的字符序列。
String completedString=builder.toString();
最终completedString中内容为"HELLO"。
(2)StringBuilder的源码:
StringBuilder的类定义:StrigBuilder继承自AbstractStringBuilder
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
AbstractStringBuilder的源码:
abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
*/
char[] value;//AbstractStringBuilder中用来是存放字符串的字符数组
/**
* The count is the number of characters used.
*/
int count;//描述当前字符数组的实际容量(即数组内字符串长度)
/**
* This no-arg constructor is necessary for serialization of subclasses.
*/
AbstractStringBuilder() {
}
/**
* Creates an AbstractStringBuilder of the specified capacity.
*/
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
由源码可知AbstractStringBuilder内部是可变数组,AbstractStringBuilder的构造函数确定了数组初始容量大小。
StrigBuilder的构造函数直接调用了父类AbstractStringBuilder的构造函数,并
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
/** use serialVersionUID for interoperability */
static final long serialVersionUID = 4383685877147921099L;
/**
* Constructs a string builder with no characters in it and an
* initial capacity of 16 characters.
*/
public StringBuilder() {
super(16);
}
/**
* Constructs a string builder with no characters in it and an
* initial capacity specified by the {@code capacity} argument.
*
* @param capacity the initial capacity.
* @throws NegativeArraySizeException if the {@code capacity}
* argument is less than {@code 0}.
*/
public StringBuilder(int capacity) {
super(capacity);
}
所以StringBuilder builder =new StringBuilder();一开始构建一个构造器,builder对象会初始化一个长度为16的char数组,内容为空。
当我们需要向builder中添加字符串时,使用append()方法。
StringBuilder的append源码(调用了父类AbstractStringBuilder的append方法):
@Override
public StringBuilder append(char[] str) {
super.append(str);
return this;
}
AbstractStringBuilder的append方法源码:
public AbstractStringBuilder append(char[] str) {
int len = str.length;
ensureCapacityInternal(count + len);
System.arraycopy(str, 0, value, count, len);
count += len;
return this;
}
调用了了ensureCapacityInternal()方法和array方法。ensureCapacityInternal(count + len)方法判断当前数组的实际容量加上要添加的字符串长度是否大于最大容量,如果大于最大容量,那么就扩容,扩容规则在方法内,扩容手段为Arrays.copyOf()。
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
//当minimumCapacity(count+len,原实际长度加新增字符串长度)大于最大容量,就使用Arrays.copyOf方法延长数组容量,具体延长长度由newCapacity()方法确定。
然后利用System.arraycopy(str, 0, value, count, len)浅复制数组,达到添加字符串的目的。