Java源码阅读之String(1)
String类表示字符串。 Java程序中的所有字符串文字(例如“abc”)都是作为这个类的实例来实现的。 String的实例创建之后值就不能被修改。StringBuffer和StringBuilder支持可变的字符串。因为字符串对象是不可变的,所以它们可以共享。String类底层是用char数组存储值的,它实现了java.io.Serializable, Comparable, CharSequence三个接口,三个接口的作用分别是:
- 1、Serializable 是一个标志接口,意思是实现了这个接口的类的对象可以被序列化。
- 2、Comparable 是实现比较大小的接口。
- 3、CharSequence接口,标志这实现了这个接口的对象是一个字符 char 序列。
这一篇博客就主要分析一下String类的构造方法。
private final char value[];//value数组用于存储字符串的值
private int hash; // 缓存字符串的哈希码 默认值为0
//无参构造方法,默认将空字符串的值赋值给新建实例
public String() {
this.value = "".value;
}
/*根据一个已有的字符串创建新的实例,这里可以看到
*只是将original的值的引用赋值给新的实例的value
*并没有将original的值复制一份
*/
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
/*根据一个已有的字符数组创建新的实例,这里可以看到
*将value数组的值复制了一份赋值给新的实例的value
*/
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
/*根据一个已有的字符数组中的指定偏移量和长度创建新的实例,这里可以看到
*将value数组的值根据偏移量和长度复制了一份赋值给新的实例的value
*offset是偏移量也就是从value数组的什么位置开始复制
*count是需要复制的长度
*/
public String(char value[], int offset, int count) {
//偏移量小于0 抛出异常
if (offset < 0) {
throw new StringIndexOutOfBoundsException(offset);
}
if (count <= 0) {
//长度小于0 抛出异常
if (count < 0) {
throw new
StringIndexOutOfBoundsException(count);
}
//长度等于0 且偏移量小于等于传入的value数组长度
//则将空字符串的值赋给新建的实例
if (offset <= value.length) {
this.value = "".value;
return;
}
}
//这里我觉得这样更好理解 offset + count > value.length
//如果截取的长度超过value数组的长度则抛出异常
if (offset > value.length - count) {
throw new StringIndexOutOfBoundsException(offset +
count);
}
//根据传入的偏移量和长度复制value数组中的值,作为一个新的数组
//赋值给新建实例的value
this.value = Arrays.copyOfRange(value, offset,
offset+count);
}
/*根据一个已有的int数组中的指定偏移量和长度创建新的实例,这里可以看到
*将codePoints数组的值根据偏移量和长度复制了一份赋值给新的实例的value
*offset是偏移量也就是从value数组的什么位置开始复制
*count是需要复制的长度
*这里的值不是简单的直接转换为char而是将int的值作为unicode编码值赋值
*给value数组
*/
public String(int[] codePoints, int offset, int count) {
//偏移量小于0 抛出异常
if (offset < 0) {
throw new StringIndexOutOfBoundsException(offset);
}
if (count <= 0) {
//长度小于0 抛出异常
if (count < 0) {
throw new
StringIndexOutOfBoundsException(count);
}
//长度等于0 且偏移量小于等于传入的value数组长度
//则将空字符串的值赋给新建的实例
if (offset <= codePoints.length) {
this.value = "".value;
return;
}
}
//这里我觉得这样更好理解 offset + count > value.length
//如果截取的长度超过value数组的长度则抛出异常
if (offset > codePoints.length - count) {
throw new StringIndexOutOfBoundsException(offset +
count);
}
//end记录需要复制到的最大位置+1
final int end = offset + count;
//n用于计算新的实例的value的长度
int n = count;
for (int i = offset; i < end; i++) {
int c = codePoints[i];
//判断当前数字是否属于BMP代码点
//有效的BMP代码点范围是 \u0000至 \uFFFF也就是说当前
//Unicode编码长度为两个字节一个char可以放下
if (Character.isBmpCodePoint(c))
continue;
//判断当前数字是否属于Unicode编码
//Unicode编码的有效范围是0x000000至0X10FFFF
//如果是Unicode编码却不是BMP编码 那么就需要两个char才能记
//录一个高位Unicode编码
else if (Character.isValidCodePoint(c))
n++;
else throw new
IllegalArgumentException(Integer.toString(c));
}
final char[] v = new char[n];
for (int i = offset, j = 0; i < end; i++, j++) {
int c = codePoints[i];
//记录一个BMP的编码
if (Character.isBmpCodePoint(c))
v[j] = (char)c;
else
//将当前数字分为高十六位和低十六位分开记录到
//v数组的两个char中
Character.toSurrogates(c, v, j++);
}
this.value = v;
}
/*检查偏移量参数offset和长度参数length是否合法
*/
private static void checkBounds(byte[] bytes, int offset,
int length) {
if (length < 0)
throw new StringIndexOutOfBoundsException(length);
if (offset < 0)
throw new StringIndexOutOfBoundsException(offset);
if (offset > bytes.length - length)
throw new StringIndexOutOfBoundsException(offset +
length);
}
/*将给定的byte数组以给定的编码方式转换为char数组复制给新建实例value
*给定的编码方式为编码名如“UTF-8”
*转换范围为offset到offset+length
*/
public String(byte bytes[], int offset, int length, String
charsetName) throws UnsupportedEncodingException {
if (charsetName == null)
throw new NullPointerException("charsetName");
//检查参数是否合法
checkBounds(bytes, offset, length);
//将给定的byte数组以给定的编码方式转换为char数组
this.value = StringCoding.decode(charsetName, bytes,
offset, length);
}
/*将给定的byte数组以给定的编码方式转换为char数组复制给新建实例value
*给定的编码方式为CharSet类型,如CharSet.forName(“UTF-8”)
*转换范围为offset到offset+length
*/
public String(byte bytes[], int offset, int length, Charset
charset) {
if (charset == null)
throw new NullPointerException("charset");
//检查参数是否合法
checkBounds(bytes, offset, length);
//将给定的byte数组以给定的编码方式转换为char数组
this.value = StringCoding.decode(charset, bytes,
offset, length);
}
/*将给定的byte数组以给定的编码方式转换为char数组复制给新建实例value
*给定的编码方式为编码名如“UTF-8”
*默认转换的bytes范围为全部
*/
public String(byte bytes[], String charsetName)
throws UnsupportedEncodingException {
//调用重载构造方法
this(bytes, 0, bytes.length, charsetName);
}
/*将给定的byte数组以给定的编码方式转换为char数组复制给新建实例value
*给定的编码方式为CharSet类型,如CharSet.forName(“UTF-8”)
*默认转换的bytes范围为全部
*/
public String(byte bytes[], Charset charset) {
//调用重载构造方法
this(bytes, 0, bytes.length, charset);
}
/*将给定的byte数组以默认的编码方式转换为char数组复制给新建实例value
*默认的编码方式为“ISO-8859-1”
*转换范围为offset到offset+length
*/
public String(byte bytes[], int offset, int length) {
checkBounds(bytes, offset, length);
this.value = StringCoding.decode(bytes, offset,
length);
}
/*将给定的byte数组以默认的编码方式转换为char数组复制给新建实例value
*默认的编码方式为“ISO-8859-1”
*转换范围为bytes全部
*/
public String(byte bytes[]) {
//调用重载构造方法
this(bytes, 0, bytes.length);
}
/*以给定的StringBuffer对象构建新的实例
*将StringBuffer的value值复制一份赋值给当前新建对象的value
*/
public String(StringBuffer buffer) {
//因为StringBuffer是线程安全的所以这里需要将
//StringBuffer对象锁住
synchronized(buffer) {
this.value = Arrays.copyOf(buffer.getValue(),
buffer.length());
}
}
/*以给定的StringBuilder 对象构建新的实例
*将StringBuilder 的value值复制一份赋值给当前新建对象的value
*/
public String(StringBuilder builder) {
this.value = Arrays.copyOf(builder.getValue(),
builder.length());
}
/*这是一个包的私有构造函数 不需要我们调用,因为String已经提供了
*一个以char[]为参数的构造函数。
*/
String(char[] value, boolean share) {
// assert share : "unshared not supported";
this.value = value;
}
String类的构造方法就是这些,几个已经被弃用的我并没有列出来,如果有兴趣还请参阅Java源码。