Class About String In Package Of Java Lang
这篇笔记主要用于分析Java中String系列的源代码。以下是String系列的的类层次结构图。这篇博文主要目的在于分析String系列源码思想、以及String系列类型之间的关系,以及性能等。
第一序 String类型源码解析
String类型是一个final类型,是不允许继承一改变的。
1.持有成员变量
一直疑惑String字符串类型为什么那么类似于字符数组char[ ],分析源码后将会发现,本质就是字符数组。String类型持有私有访问权限的字符数组。
源码:privatefinal char value[];
都发现String中的字符串是连续不可变的,也正说明该value[ ]数组是final类型。同事Java程序中将类似于“abc”这样的字符串编译为String类型的实例对象,至于这个实现细节有待于进一步学习!
同时String类型实现了Serializable序列化标记接口,所以拥有序列化ID。
源码:privatestaticfinallongserialVersionUID= -6849794470754667710L;
String类型持有一个整形,作为hash值。
源码:private int hash;
2.实例构造器
a.无参构造器
publicString() {
this.value= new char[0];
}
b.字符数组作为入参构造器
publicString(charvalue[]) {
this.value= Arrays.copyOf(value,value.length);
}
c.”abc”这样的字符串作为入参构造器
publicString(Stringoriginal) {
this.value= original.value;
this.hash= original.hash;
}
d.整形数组作为入参构造器
publicString(int[]codePoints,int offset,int count) {
if (offset< 0) {
throw new StringIndexOutOfBoundsException(offset);
}
if (count< 0) {
throw new StringIndexOutOfBoundsException(count);
}
// Note: offset or count might be near -1>>>1.
if (offset> codePoints.length- count) {
throw new StringIndexOutOfBoundsException(offset+ count);
}
final int end =offset + count;
// Pass 1: Compute precise size of char[]
int n =count;
for (inti =offset;i <end;i++) {
int c =codePoints[i];
if (Character.isBmpCodePoint(c))
continue;
else if (Character.isValidCodePoint(c))
n++;
else throw new IllegalArgumentException(Integer.toString(c));
}
// Pass 2: Allocate and fill in char[]
final char[]v =new char[n];
for (inti =offset,j = 0;i <end;i++,j++) {
int c =codePoints[i];
if (Character.isBmpCodePoint(c))
v[j] = (char)c;
else
Character.toSurrogates(c,v,j++);
}
this.value= v;
}
经过我自己的测试后发现:BmpCodePoint代码点是65535是2的16次方,刚好是两个字节(即一个字)的大小。在超出两个字节后只能算是有效的代码点,并非是BmpCodePoint代码点。从代码中也可看出,BmpCodePoint代码点的整数是可以直接强转成char类型的。在java中char类型刚好占2个字节,在2个字节以内的整数都可以直接强转换成char类型!
这里需要了解Unicode字符的原理,以及代码点的概念!
e.字节数组作为入参构造器
publicString(bytebytes[],int offset,int length, StringcharsetName)
throws UnsupportedEncodingException {
if (charsetName== null)
throw new NullPointerException("charsetName");
checkBounds(bytes,offset,length);
this.value= StringCoding.decode(charsetName,bytes,offset,length);
}
f.StringBuffer作为入参构造器
g.StringBuilder作为入参构造器
3.成员方法
getChars( int srcBegin,int srcEnd,char dst[],int dstBegin)源码:
publicvoid getChars(intsrcBegin,int srcEnd,char dst[],int dstBegin) {
if (srcBegin< 0) {
throw new StringIndexOutOfBoundsException(srcBegin);
}
if (srcEnd> value.length) {
throw new StringIndexOutOfBoundsException(srcEnd);
}
if (srcBegin> srcEnd) {
throw new StringIndexOutOfBoundsException(srcEnd- srcBegin);
}
System.arraycopy(value,srcBegin,dst,dstBegin,srcEnd - srcBegin);
}
其中调用了System类的本地方法arraycopy( ),判断是否数组越界:开始位置是否大于0,结束位置是否大于字符串长度,开始位置是否小于0。然后子调用本地方法从开始位置到结束位置将字符串复制到目的字符串中。
String的equals( )方法被重写,首先判断两个String对象是否相等,如果不等再判断两个String对象的字符是否相等。
indexOf(int ch, int fromIndex)源码:
publicint indexOf(intch,int fromIndex) {
final int max =value.length;
if (fromIndex< 0) {
fromIndex = 0;
} else if (fromIndex>= max) {
// Note: fromIndex might be near -1>>>1.
return -1;
}
if (ch< Character.MIN_SUPPLEMENTARY_CODE_POINT) {
// handle most cases here (ch is a BMP code point or a
// negative value (invalid code point))
final char[]value = this.value;
for (inti =fromIndex;i <max;i++) {
if (value[i] == ch) {
return i;
}
}
return -1;
} else {
return indexOfSupplementary(ch,fromIndex);
}
}
ch< Character.MIN_SUPPLEMENTARY_CODE_POINT 这个条件非常重要,是分界BmpCode的界限。Character.MIN_SUPPLEMENTARY_CODE_POINT是java中的16进制的表示方式,以0x开头代表十六进制,0开头的是八进制。
Character.MIN_SUPPLEMENTARY_CODE_POINT这个数代表十进制中62355,刚好是2个字节。
非BmpCode代码点是ValidCodePoint代码点的索引位置方法indexOfSupplementary(intch,intfromIndex)
源码:private int indexOfSupplementary(intch,int fromIndex) {
if (Character.isValidCodePoint(ch)) {
final char[]value = this.value;
final char hi = Character.highSurrogate(ch);
final char lo = Character.lowSurrogate(ch);
final int max =value.length- 1;
for (inti =fromIndex;i <max;i++) {
if (value[i] == hi &&value[i+ 1] ==lo) {
return i;
}
}
}
return -1;
}
int类型在java中占4个字节,非BmpCode代码点是ValidCodePoint代码点是有高两位和低两位,这种类型的int转化为字符时分开来处理,作为两个字符
String的intern( )方法分析:
这个方法是本地方法,原理是:String的intern()方法的原理,在调用此方法时候会初始化一个空的字符串池,然后将该字符串对象加入该池中,当再次调用这个方法的时候,会判断这个池中是否包含字符串,如果包含则从池中返回这个字符串,否则加入这个字符串并返回这个字符串对象的引用。
测试代码和输出结果是:
publicstaticvoid testIntern(){
System.out.println("\n测试字符串转的本地方法intern()的原理:");
String str =new String("abc");
String str0 =str.intern();
System.out.println(str == str0);
String str1=str.intern();
System.out.println(str0 == str1);
String str2 =new String("abc");
String str3 =str2.intern();
System.out.println(str0 == str3);
System.out.println("测试字符串转的本地方法intern()的原理结束\n");
}
测试字符串转的本地方法intern()的原理:
false
true
true
测试字符串转的本地方法intern()的原理结束