最近有很多空闲的时间,正好可以用来看看jdk一些常用类的源码。阅读源码可以让我在使用这些API的时候可以知其所以然,
还可以领略大师的代码。好了,废话不多说了。
1.String类的定义
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence
从定义可以看到,String类使用了final关键字修饰,这就是为什么String类不能够被继承的原因。之所以这样做,主要是为了效率和 安全性的缘故。
String也实现了Serializable接口,说明他是可序列化的。由于它也实现了Comparable接口,所以已经实现了排序的算法。
String内部的实现是通过一个char型的数组实现的,如下所示:
private final char value[];
可以看到value数组也使用了final关键字修饰,这决定了String是不可变类,final 修饰数组或对象时,对象地址不可变,但堆内存中的内容还可变。
2.String的构造方法。
String类总共有15个构造方法,所以可以通过各种各样的方式来创建一个String对象。String构造方法的文档:
构造方法摘要 | |
---|---|
String() 初始化一个新创建的 String 对象,使其表示一个空字符序列。 | |
String(byte[] bytes) 通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String 。 | |
String(byte[] bytes, Charset charset) 通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String 。 | |
String(byte[] ascii, int hibyte) 已过时。 该方法无法将字节正确地转换为字符。从 JDK 1.1 开始,完成该转换的首选方法是使用带有 Charset 、字符集名称,或使用平台默认字符集的 String 构造方法。 | |
String(byte[] bytes, int offset, int length) 通过使用平台的默认字符集解码指定的 byte 子数组,构造一个新的 String 。 | |
String(byte[] bytes, int offset, int length, Charset charset) 通过使用指定的 charset 解码指定的 byte 子数组,构造一个新的 String 。 | |
String(byte[] ascii, int hibyte, int offset, int count) 已过时。 该方法无法将字节正确地转换为字符。从 JDK 1.1 开始,完成该转换的首选方法是使用带有 Charset 、字符集名称,或使用平台默认字符集的 String 构造方法。 | |
String(byte[] bytes, int offset, int length, String charsetName) 通过使用指定的字符集解码指定的 byte 子数组,构造一个新的 String 。 | |
String(byte[] bytes, String charsetName) 通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String 。 | |
String(char[] value) 分配一个新的 String ,使其表示字符数组参数中当前包含的字符序列。 | |
String(char[] value, int offset, int count) 分配一个新的 String ,它包含取自字符数组参数一个子数组的字符。 | |
String(int[] codePoints, int offset, int count) 分配一个新的 String ,它包含 Unicode 代码点数组参数一个子数组的字符。 | |
String(String original) 初始化一个新创建的 String 对象,使其表示一个与参数相同的字符序列;换句话说,新创建的字符串是该参数字符串的副本。 | |
String(StringBuffer buffer) 分配一个新的字符串,它包含字符串缓冲区参数中当前包含的字符序列。 | |
String(StringBuilder builder) 分配一个新的字符串,它包含字符串生成器参数中当前包含的字符序列。 |
3.String的hashcode算法的实现。
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
以abc为例。根据hashcode()源码中的循环部分的算法,计算一下abc的hashcode值.a,b,c 的ascii码值分别为97,98,99;字符个数3
总共有3次循环
1:h=31*0+val[0]=val[0]='a'=97
2:h=31*h+val[1]=31*97+'b'=31*97+98=3105
3:h=31*h+val[2]=31*3166+'c'=31*3105+99=96354
所以abc的hashcode值为96354
至于为什么要选择31这个数字作为基数,网上有很多是解答,我认为主要是以下几个原因:
1. 31是质数,质数的特性(只有1和自己是因子)能够使得它和其他数相乘后得到的结果比其他方式更容易产成唯一性,也就是hash code值的冲突概率最小。
2. 选择31是观测分布结果后的一个选择,不清楚原因,但的确有利。
3. 31*N可以被编译器优化为左移5位后减1,有较高的性能。
4.String的排序算法的实现。
由于它也实现了Comparable接口,下面看看它的compareTo方法
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
可以看到,最多比较lim次,既较短的字符串的长度。从0下表的字符开始,依次比较,如果相同,继续。如果不相同,返回两个字符的差值。如果较短字符串与较长字符串的前几位相同,则返回两个字符串的长度的差值。
String类还有很多功能强大的方法,由于时间的关系,就不一一分析了。先写到这里吧。。。。。