String是我们java开发中最常用的类,在此我试着通读String源码,并写下自己的感想
String中实现了
implements java.io.Serializable, Comparable<String>, CharSequence
java.io.Serializable,表示序列化,是一个空接口,也就是说这个接口没有声明任何的方法,所以实现这个接口的类也就不需要实现任何的方法。
Comparable<String>
此接口强行对实现它的每个类的对象进行整体排序。此排序被称为该类的自然排序 ,类的 compareTo 方法被称为它的自然比较方法 。实现此接口的对象列表(和数组)可以通过 Collections.sort (和 Arrays.sort )进行自动排序。实现此接口的对象可以用作有序映射表中的键或有序集合中的元素,无需指定比较器。 强烈推荐(虽然不是必需的)使自然排序与 equals 一致。所谓与equals一致是指对于类 C 的每一个 e1 和 e2 来说,当且仅当 (e1.compareTo((Object)e2) == 0) 与e1.equals((Object)e2) 具有相同的布尔值时,类 C 的自然排序才叫做与 equals 一致 。
String 继承于CharSequence,也就是说String也是CharSequence类型。
CharSequence是一个接口,它只包括length(), charAt(int index), subSequence(int start, int end)这几个API接口。除了String实现了CharSequence之外,StringBuffer和StringBuilder也实现了CharSequence接口。
需要说明的是,CharSequence就是字符序列,String, StringBuilder和StringBuffer本质上都是通过字符数组实现的!
String声明了char数组,int型,UID,ObjectStreamField[]四个私有变量
private final char value[]; private int hash; private static final long serialVersionUID = -6849794470754667710L; private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];
其中
serialPersistentFields
尚不理解,暂且跳过.
下面是String自身的构造方法
1.
public String() { this.value = new char[0]; }
简单没有任何传参的构造方法,声明一个长度为0的char数组
2.
public String(String original) { this.value = original.value; this.hash = original.hash; }
传入一个String类型的数据,把此String装配为char[]数组,长度传为int.
其实很奇怪的是,String中可以自己调取自己吗..
3.
public String(char value[]) { this.value = Arrays.copyOf(value, value.length); }
传入一个char数组,通过copyOf方法复制此数据
其实有点搞不懂哎,为啥不直接传值,而是通过Arrays来传入
4.
public String(char value[], 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 > value.length - count) { throw new StringIndexOutOfBoundsException(offset + count); } this.value = Arrays.copyOfRange(value, offset, offset+count); }
传入char数组,两个int参数,如果int小于0或者offset大于数组本身长度减去count则返回异常
否则使用copyOfRange函数,复制一个传入数组offset----(offset+count)范围的数组
5.
public String(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 (int i = 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 (int i = 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; }
传入unicode字符集
前面首先判断是否有负值或者非0值,如果没有则声明
final int end = offset+count;
int n =count;
for (int i = 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)); }
以offset为起点遍历count长度的传入int数组
循环判断编码类型
如果为Bmp则跳过循环,如果是valid则使n+1
final char[] v = new char[n];
完成循环后,声明一个以当前n值为长度的,其中如含有bmp类型的则为正常长度,为valid但不为bmp的为两个长度
char数组
for (int i = 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++); }
此时继续进行循环
以offset为基准,遍历数组
经过我自己的测试后发现:BmpCodePoint代码点是65535是2的16次方,刚好是两个字节(即一个字)的大小。在超出两个字节后只能算是有效的代码点,并非是BmpCodePoint代码点。从代码中也可看出,BmpCodePoint代码点的整数是可以直接强转成char类型的。在java中char类型刚好占2个字节,在2个字节以内的整数都可以直接强转换成char类型
---------------------------------------
其实就是根据输入的unicode编码转换字符串,如果超出unicdoe的编码范围,则去取最大值0xffff.
6.
public String(byte ascii[], int hibyte, int offset, int count) { checkBounds(ascii, offset, count); char value[] = new char[count]; if (hibyte == 0) { for (int i = count; i-- > 0;) { value[i] = (char)(ascii[i + offset] & 0xff); } } else { hibyte <<= 8; for (int i = count; i-- > 0;) { value[i] = (char)(hibyte | (ascii[i + offset] & 0xff)); } } this.value = value; }
checkBounds(ascii, offset, count);
校检长度是否符合规则
如果输入的长度符合规则
则声明一个count长度的char数组
value[i] = (char)(ascii[i + offset] & 0xff);&
按位与
比如以下例子
System.out.println(0x55 & 0xff); System.out.println(0xff); System.out.println(0x55);85
255
85
0x55=01010101
0xff=11111111
=01010101
实际上来看,如果按位与0xff,并不会对返回的结果产生实质上的反应,这个反而是为了防止溢出ASCII码吧
hibyte <<= 8;
位移动符
int a =512; a >>= 3; System.out.println(64);
相当于512^1/2^3
7.
public String(byte ascii[], int hibyte) { this(ascii, hibyte, 0, ascii.length); }
this(ascii, hibyte, 0, ascii.length);是调取上一个方法
8.
public String(byte bytes[], int offset, int length, String charsetName) throws UnsupportedEncodingException { if (charsetName == null) throw new NullPointerException("charsetName"); checkBounds(bytes, offset, length); this.value = StringCoding.decode(charsetName, bytes, offset, length); }
static char[] decode(String charsetName, byte[] ba, int off, int len) throws UnsupportedEncodingException { StringDecoder sd = deref(decoder); String csn = (charsetName == null) ? "ISO-8859-1" : charsetName; if ((sd == null) || !(csn.equals(sd.requestedCharsetName()) || csn.equals(sd.charsetName()))) { sd = null; try { Charset cs = lookupCharset(csn); if (cs != null) sd = new StringDecoder(cs, csn); } catch (IllegalCharsetNameException x) {} if (sd == null) throw new UnsupportedEncodingException(csn); set(decoder, sd); } return sd.decode(ba, off, len); }
看不懂