今年终于重新回到开发岗位继续我的程序员生涯,但时隔一年半,很多都有些生疏了,仔细读了一遍<Thinking In Java>,发现很多基本技术和思想其实从一开始就没研究明白。决定重新对照着书本,jdk和源码把Java中基本的类库研读总结下。
第一篇就先从最简单最基本的String开始吧!
博客的对代码的排版似乎不好,附件里附上txt格式的内容,可读性会好些。
1.String的基本特性
我们在class中找到java.lang.String,先来看String的基本特性。
String类的定义是这样的:public final class String implements java.io.Serializable, Comparable<String>, CharSequence
从以上定义,我们可以看出String的几个基本特性:
·String是一个类。(似乎是废话。主要与其他基本类型分开,Java中的类型似乎只有String与Enum是类形式定义的。)
·String是只读的常量,具有不可变性;不可被继承。(修饰为final)
·String类型是可序列化的,可比较的字符序列。(implements java.io.Serializable, Comparable<String>, CharSequence)
再来看一下Sting类中的定义的几个基本field
private final char value[];//String的值,String的本质就是char型数组
private final int offset;//子数组第一个字符的索引
private final int count;//字符串中的字符数量
private int hash; //对象的hash code
经过上面的分析,其实我们已经能看出String对象的本质是一个经过封装的char型数组,封装后提供了一些方便操作的方法。
2.Sting常用方法及实现(大部分注释选自JavaSE5中文API说明)
2.1.equals(Object anObject)
/**
* 重写equals方法 比较此字符串与指定的对象。
* 当且仅当该参数不为 null,并且是表示与此对象相同的字符序列的 String对象时,结果才为true。
* @param anObject 与此 String 进行比较的对象。
* @return 如果 String 相等,则返回 true;否则返回 false。
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {// 先判断类型
String anotherString = (String) anObject;
int n = count;
if (n == anotherString.count) {// 再判断数组容量是否相同
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {// 再遍历判断每一个字符
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
2.2.compareTo(String anotherString)方法
/**
* 重写compreTo方法 按字典顺序比较两个字符串。 该比较基于字符串中各个字符的 Unicode 值。 将此 String
* 对象表示的字符序列与参数字符串所表示的字符序列进行比较。如果按字典顺序此 String 对象在参数字符串之前,则比较结果为一个负整数。
* 如果按字典顺序此 String 对象位于参数字符串之后,则比较结果为一个正整数。 如果这两个字符串相等,则结果为 0;compareTo
* 只有在方法 equals(Object) 返回 true 时才返回 0。 这是字典排序的定义。
* 如果这两个字符串不同,则要么它们在某个索引处具有不同的字符,该索引对二者均为有效索引;要么它们的长度不同;或者同时具备上述两种情况。
* 如果它们在一个或多个索引位置上具有不同的字符,
* 假设 k 是这类索引的最小值;则按照 < 运算符确定的那个字符串在位置 k上具有较小的值,其字典顺序在其他字符串之前。
* 这种情况下,compareTo 返回这两个字符串在位置 k 处的两个不同的 char 值,即值:
* this.charAt(k)-anotherString.charAt(k)
* 如果它们没有不同的索引位置,则较短字符串在字典顺序上位于较长字符串的前面。 这种情况下,compareTo 返回这两个字符串长度的不同,即值:
* this.length()-anotherString.length()
* @param anotherString 要比较的 String。
* @return 如果参数字符串等于此字符串,则返回 0 值;
* 如果按字典顺序此字符串小于字符串参数,则返回一个小于 0 的值;
* 如果按字典顺序此字符串大于字符串参数,则返回一个大于 0 的值。
*/
public int compareTo(String anotherString) {
int len1 = count;
int len2 = anotherString.count;
int n = Math.min(len1, len2);// 获得两个字符串中较短的长度
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
/* 在一个或多个索引位置上具有不同的字符 */
if (i == j) {// 偏移量相同
int k = i;
int lim = n + i;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
} else {
while (n-- != 0) {
char c1 = v1[i++];
char c2 = v2[j++];
if (c1 != c2) {
return c1 - c2;
}
}
}
/* 没有不同的索引位置 */
return len1 - len2;
}
前面的注释中,已经把String的排序规则说的很明白了。简单来说就是:1.先判断每个索引处的字符都相同,找到第一个字符不同处的索引,返回此索引处两个字符的差;2.如果每个索引处的字符都相同,判断两个数组的长度,返回两个字符串的长度差。
2.3.startsWith(String prefix, int toffset)
/**
* 测试此字符串是否以指定前缀开始,该前缀以指定索引开始。
* @param prefix 前缀
* @param toffset 在字符串中开始查找的位置。
* @return 如果该参数表示的字符序列是此对象从索引 toffset 处开始的子字符串,则返回 true;否则返回 false。
* 如果 toffset 为负或大于此 String 对象的长度,则结果为 false;否则结果与该表达式的结果相同。
*/
public boolean startsWith(String prefix, int toffset) {
char ta[] = value;
int to = offset + toffset;
char pa[] = prefix.value;
int po = prefix.offset;
int pc = prefix.count;
// Note: toffset might be near -1>>>1.
if ((toffset < 0) || (toffset > count - pc)) {
return false;
}
while (--pc >= 0) {//遍历前缀的字符,与当前字符串进行比较
if (ta[to++] != pa[po++]) {
return false;
}
}
return true;
}
这个方法的实现很简单,需要注意的就是toffset的值不要为负或者大于String对象的长度。
2.4.String substring(int beginIndex, int endIndex)
public String substring(int beginIndex, int endIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
if (endIndex > count) {
throw new StringIndexOutOfBoundsException(endIndex);
}
if (beginIndex > endIndex) {
throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
}
return ((beginIndex == 0) && (endIndex == count)) ? this : new String(offset + beginIndex, endIndex - beginIndex, value);
}
很常用的一个方法,会截取从beginIndex处到endIndex处的字符串,生成一个新的字符串。从方法实现可以看出,要注意的有两点:1.beginIndex和endIndex两个参数要符合要求,否则会抛出StringIndexOutOfBoundsException。2.截取的字符串包括beginIndex处的字符,不包括endIndex处的字符。
2.5.String trim()
/**
* 返回字符串的副本,忽略前导空白和尾部空白。
* 如果此 String 对象表示一个空字符序列,或者此 String 对象表示的字符序列的第一个和最后一个字符的代码都大于 '\u0020'(空格字符),
* 则返回对此 String 对象的引用。
* 否则,若字符串中没有代码大于 '\u0020' 的字符,则创建并返回一个表示空字符串的新的 String 对象。
* 否则,假定 k 为代码大于 '\u0020' 的第一个字符的索引,m 为代码大于 '\u0020' 的最后一个字符的索引。
* 创建一个新的 String 对象,它表示此字符串中从索引 k 处的字符开始,到索引 m 处的字符结束的子字符串,也就是 this.substring(k, m+1) 的结果。
* 此方法用于截去字符串从头到尾的空白(如上面所定义)。
* @return 此字符串移除了前导和尾部空白的副本,如果没有前导和尾部空白,则返回此字符串。
*/
public String trim() {
int len = count;
int st = 0;
int off = offset;
char[] val = value;
while ((st < len) && (val[off + st] <= ' ')) {
st++;
}
while ((st < len) && (val[off + len - 1] <= ' ')) {
len--;
}
return ((st > 0) || (len < count)) ? substring(st, len) : this;
}
public static String valueOf(float f) {
return Float.toString(f);
}