在String的源碼上有這麼一段註釋:
Strings are constant; their values cannot be changed after they
are created. String buffers support mutable strings.
Because String objects are immutable they can be shared.
字符串是常量,在創建後,它們的值是不能改變的。字符串緩衝區支持可變字符串,因爲字符串對象是不變的它們可以共享(即StringBuffer)。
String str = “abc”;等價於:
char data[] = {‘a’, ‘b’, ‘c’};
String str = new String(data);
一、String的定義
//在Java中String類其實就是對字符數組的封裝
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
//定義的主要變量:
/** The value is used for character storage. */
private final char value[];//常量,在構造器中初始化
/** Cache the hash code for the string */
private int hash; // Default to 0
//默認構造函數,使用此構造函數是不必要的,因爲字符串是不可變的。
public String() {
this.value = new char[0];
}
//新建的字符串是original的副本,如非必要,這個構造函數是不必要的,因爲字符串是不可變的。
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
//char數組的值拷貝到當前char數組中,char數組的變化不會影響新建的字符串。
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
//這裏還有幾個byte數組轉字符串的構造函數沒有列出
//StringBuffer裏面的字符串也是已char[] value的形式存儲的,只不過StringBuffer是線程安全
//的,需要加
public String(StringBuffer buffer) {
synchronized(buffer) {
this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
}
}
}
二、String的常用方法
1、獲取字符串相關信息的操作
//字符串的長度,即char[]的長度
public int length() {
return value.length;
}
public boolean isEmpty() {
return value.length == 0;
}
//返回指定位置的char值
public char charAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];
}
//通過特定的charset把當前字符串轉變成char數組
public byte[] getBytes(String charsetName)
throws UnsupportedEncodingException {
if (charsetName == null) throw new NullPointerException();
return StringCoding.encode(charsetName, value, 0, value.length);
}
//與特定的對象比較,該對象不爲空並且是一個字符串,具有相同的字符序列,則返回true。
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數組進行對比,相等就繼續比較,不等先轉成大寫的char值比較,不然再轉車些小的char值比較。
public boolean equalsIgnoreCase(String anotherString) {
return (this == anotherString) ? true
: (anotherString != null)
&& (anotherString.value.length == value.length)
&& regionMatches(true, 0, anotherString, 0, value.length);
}
//從字符串的char數組的toffset位置開始,與prefix比較,如果個數等於prefix.length()的char值與
//prefix的char值相等,則字符串是從 toffset以prefix爲開頭的字符串
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;
}
public boolean startsWith(String prefix) {
return startsWith(prefix, 0);
}
public boolean endsWith(String suffix) {
return startsWith(suffix, value.length - suffix.value.length);
}
//s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
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;
}
//返回指定字符的在該字符串中的第一個位置的索引
// ch a character (Unicode code point)
public int indexOf(int ch) {
return indexOf(ch, 0);//正序遍歷查找
}
//返回指定字符的在該字符串中的最後一個位置的索引
public int lastIndexOf(int ch) {
return lastIndexOf(ch, value.length - 1);//倒序遍歷查找
}
public int indexOf(String str) {
return indexOf(str, 0);
}
public boolean contains(CharSequence s) {
return indexOf(s.toString()) > -1;
}
2、字符串的增刪改操作
上面的是一些獲取信息的基本操作,下面的操作源代碼則反映了string的不可變性:
public String substring(int beginIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
int subLen = value.length - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}
public String substring(int beginIndex, int endIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
if (endIndex > value.length) {
throw new StringIndexOutOfBoundsException(endIndex);
}
int subLen = endIndex - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return ((beginIndex == 0) && (endIndex == value.length)) ? this
: new String(value, beginIndex, subLen);
}
//將指定的字符串拼接在當前字符串之後,先把當前字符串的char數組copy到新的char數組buf中,在使用
//getChars方法把str的char數組添加到buf中,最後生成一個新的string並返回。
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
public String replace(char oldChar, char newChar) {
if (oldChar != newChar) {
int len = value.length;
int i = -1;
char[] val = value; /* avoid getfield opcode */
while (++i < len) {
if (val[i] == oldChar) {
break;
}
}
if (i < len) {
char buf[] = new char[len];
for (int j = 0; j < i; j++) {
buf[j] = val[j];
}
while (i < len) {
char c = val[i];
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
return new String(buf, true);
}
}
return this;
}
不僅在這幾個方法,包括toUpperCase、toLowerCase、trim等方法都是自己本身或者調用的方法先對char數組進行操作,操作完成後生成一個新的String對象並返回。
小結:
1、String引用的的char數組value是一個private final定義的變量,我們沒有權限改變這個數組的內容(反射機制是可以操作這個value引用的值的,但是除非腦子有病纔會這麼做吧),也不能改變這個char數組的引用(final的特性,參考final關鍵字)。
2、字符串的增刪改等改變操作,都是自己本身或者調用的方法先對char數組進行操作,操作完成後生成一個新的String對象並返回。