java源码阅读String

1类签名与注释

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence

String类被定义为final类型的,所以String对象一旦创建了,就是不可变的。

String类实现了Serializable接口,表示可以序列化。

String类实现了Comparable<String>接口,表示String类型可以相互比较。(通过compareTo方法)

String类实现了CharSequence接口,CharSequence是char值的可读序列。

String类代表字符串。 Java程序中的所有字符串文字(例如"abc" )都被实现为此类的实例。Strings是不可变的,它们的值在创建后不能被更改。字符串缓冲区支持可变字符串。

Java语言为字符串连接运算符(+)提供特殊支持,并为其他对象转换为字符串。 字符串连接是通过StringBuilder (或StringBuffer )类及其append方法实现的。 字符串转换是通过toString方法来实现,该方法定义在Object类中。

2基本属性

private final char value[];

private int hash; // Default to 0

String用字符数组存储值,内部还保留了自己的hash值。为什么要将hash值作为成员属性呢?我们先看一下String的hashCode方法

 1 public int hashCode() {
 2         int h = hash;
 3         if (h == 0 && value.length > 0) {
 4             char val[] = value;
 5 
 6             for (int i = 0; i < value.length; i++) {
 7                 h = 31 * h + val[i];
 8             }
 9             hash = h;
10         }
11         return h;
12     }

当调用String的hashCode方法时,首先会从hash属性取值,当hash属性值为0时(第一次调用该方法),才会重新计算hash值。hash值得计算如line7所示。计算后会将值赋给hash属性。这里保证只在第一次调用hashCode方法的时候计算hash值,之后直接从hash属性取值。这种优化只有当String是final的时候才可以,若String可变的话,hash值也会变化。

3构造方法

String的构造方法有很多,下面是eclipse中outline的所有构造方法的截图。

红色方框部分表示废弃方法(我的版本JDK1.8)。通过outline可以看出,String可以通过String、字符数组、int数组、字节数组、StringBuffer/StringBuilder构造。下面贴出几个常用构造方法的代码。

//1构造空String
public String() {
        this.value = "".value;
    }

//2通过String构造
public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }

//3通过char数组构造
public String(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
    }

//4通过byte数组构造
 public String(byte bytes[]) {
        this(bytes, 0, bytes.length);
    }
public String(byte bytes[], int offset, int length) {
        checkBounds(bytes, offset, length);
        this.value = StringCoding.decode(bytes, offset, length);
    }

//5通过字符串缓冲区构造
public String(StringBuffer buffer) {
        synchronized(buffer) {
            this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
        }
    }
 public String(StringBuilder builder) {
        this.value = Arrays.copyOf(builder.getValue(), builder.length());
    }

这里有几点自己的理解:

(1)在通过char数组构造的时候,是通过Arrays的copyOf复制了一个新的数组(开辟了新的数组存储空间),这里不是通过简单的引用传递。

(2)byte数组不能直接变成char数组,需要通过StringCodingdecode方法。

(3)注意通过StringBuffer构造的时候加了同步,因为StringBuffer是线程安全的,这里复制的时候加锁也要保证线程安全

4常用方法

(1)equals方法

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

只有满足类型一样,长度相等,char数组所有的值都相等时equals方法才返回true。

(2)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;
    }

挨个比较字符数组的值,当较短的串所有字符都和较长串的相等时返回串的长度差。当compareTo方法的返回值等于0时,表示被比较的字符串和当前字符串相等,返回值大于0时表示当前字符串较大,返回值小于0表示当前字符串较小。

(3)startsWith和endsWith

//startsWith
public boolean startsWith(String prefix) {
        return startsWith(prefix, 0);
    }

public boolean startsWith(String prefix, int toffset) {
        char ta[] = value;
        int to = toffset;
        char pa[] = prefix.value;
        int po = 0;
        int pc = prefix.value.length;
        // Note: toffset might be near -1>>>1.
        if ((toffset < 0) || (toffset > value.length - pc)) {
            return false;
        }
        while (--pc >= 0) {
            if (ta[to++] != pa[po++]) {
                return false;
            }
        }
        return true;
    }

//endsWith
public boolean endsWith(String suffix) {
        return startsWith(suffix, value.length - suffix.value.length);
    }

starsWhith方法是从偏移量开始,挨个比较自身字符数组的值和参数对应的字符数组的值。endsWith用长度差当作偏移量。

(4)indexOf(String)方法

 1 public int indexOf(String str) {
 2         return indexOf(str, 0);
 3     }
 4 
 5 public int indexOf(String str, int fromIndex) {
 6         return indexOf(value, 0, value.length,
 7                 str.value, 0, str.value.length, fromIndex);
 8     }
 9 
10 static int indexOf(char[] source, int sourceOffset, int sourceCount,
11             String target, int fromIndex) {
12         return indexOf(source, sourceOffset, sourceCount,
13                        target.value, 0, target.value.length,
14                        fromIndex);
15     }
16 
17 static int indexOf(char[] source, int sourceOffset, int sourceCount,
18             char[] target, int targetOffset, int targetCount,
19             int fromIndex) {
20         if (fromIndex >= sourceCount) {
21             return (targetCount == 0 ? sourceCount : -1);
22         }
23         if (fromIndex < 0) {
24             fromIndex = 0;
25         }
26         if (targetCount == 0) {
27             return fromIndex;
28         }
29 
30         char first = target[targetOffset];
31         int max = sourceOffset + (sourceCount - targetCount);
32 
33         for (int i = sourceOffset + fromIndex; i <= max; i++) {
34             /* Look for first character. */
35             if (source[i] != first) {
36                 while (++i <= max && source[i] != first);
37             }
38 
39             /* Found first character, now look at the rest of v2 */
40             if (i <= max) {
41                 int j = i + 1;
42                 int end = j + targetCount - 1;
43                 for (int k = targetOffset + 1; j < end && source[j]
44                         == target[k]; j++, k++);
45 
46                 if (j == end) {
47                     /* Found whole string. */
48                     return i - sourceOffset;
49                 }
50             }
51         }
52         return -1;
53     }

主要实现在line17-53,这里就是挨个对比匹配。通过indexOf(String str)调用line17的方法是没有什么问题,因为sourceOffset偏移量总是为0,但是看源码的时候总感觉max(line31)的逻辑会导致数组越界问题。举个例子如下:

public class Test {

    public static void main(String[] args) {
        
        String a = "abc1234";
        char[] aa = {'a','b','c','1','2','3','4'};
        String b = "25";
        char[] bb = {'2','5'};
        System.out.println(indexOf(aa,2,aa.length,
                bb,0,bb.length,0));
    
    }
    
    static int indexOf(char[] source, int sourceOffset, int sourceCount,
            char[] target, int targetOffset, int targetCount,
            int fromIndex) {
              //省略...
        }

      
}
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 7
    at com.ouym.test.Test.indexOf(Test.java:44)
    at com.ouym.test.Test.main(Test.java:19)

(5)

猜你喜欢

转载自www.cnblogs.com/ouym/p/8981074.html