解决问题
- 知道String是如何实现
- String的相关构造方法以及实现
基本概念
- String类被final关键字修饰,String类不能被继承,所有成员方法都默认为final方法;
- 实现了Serializable、CharSequence、 Comparable接口。
- 内部实现字符数组。
内部实现 - 使用Char数组
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
// 缓存字符串的值
private final char value[];
/** Cache the hash code for the string */
// 缓存 HASH 值
private int hash; // Default to 0
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;
JDK1.8的String内部是实现了一个char数组来缓存字符串的值,如上代码。
个别重要的构造方法
1 - 普通构造
深拷贝了一个String对象,将入参的value,hash进行copy。
// 普通构造, 参数就是String
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
2 - char[]字符数组构造
通过 Arrays.copyOf 方法拷贝字符数组,去构建字符串
// 将 char数组作为参数
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
还是通过字符数组来构建一个字符串,不过加入了初始下标offset与字符偏移量count来进行截取构建。
// 将char数组作为参数,
// offset表示第一个被截取的字符在数组value[]中的下标
// count表示从此字符开始向后截取的字符的数量
// 将value[]数组按照传入的下标和指定的截取数组数据的数量进行截取
public String(char value[], int offset, int count) {
// 下标小于0则越界
if (offset < 0) {
throw new StringIndexOutOfBoundsException(offset);
}
// 向后截取数量长度≤0越界
if (count <= 0) {
if (count < 0) {
throw new StringIndexOutOfBoundsException(count);
}
// 如果符合字符数据的长度内, 同时count=0
// 那么就是数组的原来长度,不用截取
if (offset <= value.length) {
this.value = "".value;
return;
}
}
// 下标位置不能大于 数组长度-要截取的长度,否则就越界
// Note: offset or count might be near -1>>>1.
if (offset > value.length - count) {
throw new StringIndexOutOfBoundsException(offset + count);
}
// 符合条件就截取返回
this.value = Arrays.copyOfRange(value, offset, offset+count);
}
3 - bytes[]字节构造
编码一个字节数组,offset用来设定初始下标,再截取length个长度来构建字符串,charsetName是字符编码的格式
// 同上
// 加入了参数 charsetName,指定字符编码
public String(byte bytes[], int offset, int length, String charsetName)
throws UnsupportedEncodingException {
if (charsetName == null)
throw new NullPointerException("charsetName");
// 检查bytes数组合法性
checkBounds(bytes, offset, length);
this.value = StringCoding.decode(charsetName, bytes, offset, length);
}
构建思路同上,字符编码格式参数由 String 换成 Charset
// 同上
public String(byte bytes[], int offset, int length, Charset charset) {
if (charset == null)
throw new NullPointerException("charset");
checkBounds(bytes, offset, length);
this.value = StringCoding.decode(charset, bytes, offset, length);
}
省略了charsetName字符编码格式参数,使用系统默认编码。
// 将字节作为参数,将bytes解码成字符数组
// offset 要解码的第一个 byte 的下标
// length 要解码的 byte 数 的长度
public String(byte bytes[], int offset, int length) {
// 检查bytes数组合法性
checkBounds(bytes, offset, length);
this.value = StringCoding.decode(bytes, offset, length);
}
这两种构造方法不进行指定长度截取,只按照字符编码格式进行编码
// String编码格式
public String(byte bytes[], String charsetName)
throws UnsupportedEncodingException {
this(bytes, 0, bytes.length, charsetName);
}
// Charset
public String(byte bytes[], Charset charset) {
this(bytes, 0, bytes.length, charset);
}
默认系统编码格式,不截取bytes数组构建字符串
// 将字节作为参数
public String(byte bytes[]) {
this(bytes, 0, bytes.length);
}
4 - String Buffer构造
同一时间只允许一个线程对这个 buffer 构建成String对象,所以是线程安全的。
// 将StringBuffer 作为参数
// 这里用到了 synchronized 关键字,线程安全
public String(StringBuffer buffer) {
synchronized(buffer) {
this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
}
}
5 - String build构造
将StringBuilder通过Arrays.copyOf构建一个字符串,没有使用synchronized修饰,所以是非线程安全的。
// 将StringBuilder作为参数
public String(StringBuilder builder) {
this.value = Arrays.copyOf(builder.getValue(), builder.length());
}
总结
String类有多种构造方法,如下几个较为主要,建议常记:
- 通过深拷贝一个String对象。
- 通过字符数组进行构建。
- 通过字符数组构建,并设置起始下标与偏移量截取。
- 通过字节数组构建,并设置起始下标与偏移量截取,可以指定字节编码格式,未指定就默认系统编码格式。
- 通过StringBuffer构建,用synchronized修饰,是线程安全的。
- 通过StringBuilder构建,是非线程安全的。
下期文章继续深入分析String类。