thinking in java (三十三) ----- String詳解
String介紹
String是java中的字串,繼承於CharSequence。
String類包含的API介面非常多,我們對其進行了分類,並且都給出了演示程式。
- String個CharSequence關係
String繼承於CharSequence,也就是說String是CharSequence型別,CharSequence是一個介面,只包括length().charAt(),subSequence()幾個API介面。除了String外,StringBuffer和StringBuilder也實現了CharSequence介面。
需要說明的人,CharSequence是字元序列,String,StringBuffer和StringBuilder本質上都是通過字元陣列實現的
- StringBuffer和StringBuilder的區別
兩者都是可變的字元序列,都繼承於AbstractStringBuilder,實現了CharSequence介面,其中,StringBuilder是非執行緒安全的,StringBuffer是執行緒安全的。
關係如下:
CharSequence和String原始碼
CharSequence
package java.lang; public interface CharSequence { int length(); char charAt(int index); CharSequence subSequence(int start, int end); public String toString(); }
String
實現介面:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
- 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 static final Comparator<String> CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator();
可以看出value[]是儲存String內容的,即當String=“abc”時,本質上,abc是儲存在一個數組中的,
而hash是String例項化的hashcode的一個快取,因為String常用來比較,比如在hashmap中,如果每次比較都會重新計算一個hashcode值的話,會比較麻煩,而儲存一個hashcode的快取能夠優化這樣的操作。
最後CASE_INSENSITIVE_ORDER 是一個靜態內部類,用來比較忽略大小寫的兩個字串
內部類:
private static class CaseInsensitiveComparator
implements Comparator<String>, java.io.Serializable {
// use serialVersionUID from JDK 1.2.2 for interoperability
private static final long serialVersionUID = 8575799808933029326L;
public int compare(String s1, String s2) {
int n1 = s1.length();
int n2 = s2.length();
int min = Math.min(n1, n2);
for (int i = 0; i < min; i++) {
char c1 = s1.charAt(i);
char c2 = s2.charAt(i);
if (c1 != c2) {
c1 = Character.toUpperCase(c1);
c2 = Character.toUpperCase(c2);
if (c1 != c2) {
c1 = Character.toLowerCase(c1);
c2 = Character.toLowerCase(c2);
if (c1 != c2) {
// No overflow because of numeric promotion
return c1 - c2;
}
}
}
}
return n1 - n2;
}
/** Replaces the de-serialized object. */
private Object readResolve() { return CASE_INSENSITIVE_ORDER; }
}
String中的 一個內部類,是在忽略大小寫是,兩個例項字串的比較,可以看到,String類中提供的compareToIgnoreCase方法實際上就是嗲用這個內部類裡面的方法實現的,這就是程式碼複用的一個例子。
方法:
構造方法
public String() {
this.value = "".value;
}
有多個建構函式,包括有引數String,char[],byte[],StringBuffer等多種引數型別的建構函式,但是本質上就是講接受道的引數傳遞給全域性變數value[]。
public int length() {
return value.length;
}
public boolean isEmpty() {
return value.length == 0;
}
public char charAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];
}
我們知道了String是由字元陣列實現的,那麼就可以發現,其中的length,isEmpty,charAt都是內部呼叫陣列的方法,
//將字串複製到dst陣列中,複製到dst陣列中的起始位置可以指定。值得注意的是,該方法並沒有檢測複製到dst陣列後是否越界。
void getChars(char dst[], int dstBegin) {
System.arraycopy(value, 0, dst, dstBegin, value.length);
}
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
if (srcBegin < 0) {
throw new StringIndexOutOfBoundsException(srcBegin);
}
if (srcEnd > value.length) {
throw new StringIndexOutOfBoundsException(srcEnd);
}
if (srcBegin > srcEnd) {
throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
}
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
可以看到,這個兩個過載方法本質上都是呼叫System.arraycopy()這個函式,包括在jdk很多其他原始碼中都是這樣,比如ThreadPoolExcuter,看似有很多個過載,其實本質上都是呼叫同樣的一個函式,只是會給你不同的預設初始值。
//獲取當前字串的二進位制
public void getBytes(int srcBegin, int srcEnd, byte dst[], int dstBegin) {
if (srcBegin < 0) {
throw new StringIndexOutOfBoundsException(srcBegin);
}
if (srcEnd > value.length) {
throw new StringIndexOutOfBoundsException(srcEnd);
}
if (srcBegin > srcEnd) {
throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
}
Objects.requireNonNull(dst);
int j = dstBegin;
int n = srcEnd;
int i = srcBegin;
char[] val = value; /* avoid getfield opcode */
while (i < n) {
dst[j++] = (byte)val[i++];
}
}
public byte[] getBytes(String charsetName)
throws UnsupportedEncodingException {
if (charsetName == null) throw new NullPointerException();
return StringCoding.encode(charsetName, value, 0, value.length);
}
public byte[] getBytes() {
return StringCoding.encode(value, 0, value.length);
}
將String字串轉成二進位制的幾種方式,可以指定byte陣列,也能讓其返回一個byte陣列。本質上,其實都是呼叫了StringCoding.encode()這個靜態方法。
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;
}
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;
}
hashCode()和equals()兩個方法比較重要且有所關係就放一起了,equals()是string能成為廣泛用於Map[key,value]中key的關鍵所在。
此外除equals()外,還有隻比較內容的contentEquals();
public boolean contentEquals(CharSequence cs) {
// Argument is a StringBuffer, StringBuilder
if (cs instanceof AbstractStringBuilder) {
if (cs instanceof StringBuffer) {
synchronized(cs) {
return nonSyncContentEquals((AbstractStringBuilder)cs);
}
} else {
return nonSyncContentEquals((AbstractStringBuilder)cs);
}
}
// Argument is a String
if (cs instanceof String) {
return equals(cs);
}
// Argument is a generic CharSequence
char v1[] = value;
int n = v1.length;
if (n != cs.length()) {
return false;
}
for (int i = 0; i < n; i++) {
if (v1[i] != cs.charAt(i)) {
return false;
}
}
return true;
}
這個主要是用來比較String和StringBuffer或者StringBuild的內容是否一樣。可以看到傳入引數是CharSequence ,這也說明了StringBuffer和StringBuild同樣是實現了CharSequence。原始碼中先判斷引數是從哪一個類例項化來的,再根據不同的情況採用不同的方案,不過其實大體都是採用上面那個for迴圈的方式來進行判斷兩字串是否內容相同。
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;
}
這個就是String對Comparable介面中方法的實現了。其核心就是那個while迴圈,通過從第一個開始比較每一個字元,當遇到第一個較小的字元時,判定該字串小。
但還有一種是在較小長度的字元粗每個字元都和另一個字串的每個字元相等,那麼字串長度較大的較大。
public int compareToIgnoreCase(String str) {
return CASE_INSENSITIVE_ORDER.compare(this, str);
}
這個也是比較字串大小,規則和上面那個比較方法基本相同,差別在於這個方法忽略大小寫。可以看到這是通過一個String 內部一個static的內部類實現的,那麼為什麼還要特地寫一個內部類呢,這樣其實就是為了程式碼複用,這樣在其他情況下也可以使用這個static內部類。
public boolean regionMatches(int toffset, String other, int ooffset,
int len) {
char ta[] = value;
int to = toffset;
char pa[] = other.value;
int po = ooffset;
// Note: toffset, ooffset, or len might be near -1>>>1.
if ((ooffset < 0) || (toffset < 0)
|| (toffset > (long)value.length - len)
|| (ooffset > (long)other.value.length - len)) {
return false;
}
while (len-- > 0) {
if (ta[to++] != pa[po++]) {
return false;
}
}
return true;
}
比較該字串和其他一個字串從分別指定地點開始的n個字元是否相等。看程式碼可知道,其原理還是通過一個while去迴圈對應的比較區域進行判斷,但在比較之前會做判定,判定給定引數是否越界。
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;
}
判斷當前字串是否以某一段其他字串開始的,和其他字串比較方法一樣,其實就是通過一個while來迴圈比較。
public int indexOf(int ch, int fromIndex) {
final int max = value.length;
if (fromIndex < 0) {
fromIndex = 0;
} else if (fromIndex >= max) {
// Note: fromIndex might be near -1>>>1.
return -1;
}
if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
// handle most cases here (ch is a BMP code point or a
// negative value (invalid code point))
final char[] value = this.value;
for (int i = fromIndex; i < max; i++) {
if (value[i] == ch) {
return i;
}
}
return -1;
} else {
return indexOfSupplementary(ch, fromIndex);
}
}
public int indexOf(int ch) {
return indexOf(ch, 0);
}
可以看到這裡在if中有一句
ch < Character.MIN_SUPPLEMENTARY_CODE_POINT 而在Character中看到
public static final int MIN_SUPPLEMENTARY_CODE_POINT = 0x010000; 這表明在java中char儲存的值通常都是比ox010000小的,就是BMP型別的字元。 而當比這個值大的時候,就是增補字元了,那麼會呼叫Character先判斷是否是有效的字元,再進一步處理
public int lastIndexOf(int ch, int fromIndex) {
if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
// handle most cases here (ch is a BMP code point or a
// negative value (invalid code point))
final char[] value = this.value;
int i = Math.min(fromIndex, value.length - 1);
for (; i >= 0; i--) {
if (value[i] == ch) {
return i;
}
}
return -1;
} else {
return lastIndexOfSupplementary(ch, fromIndex);
}
}
和indexOf基本一致,只是順序反過來。
static int indexOf(char[] source, int sourceOffset, int sourceCount,
char[] target, int targetOffset, int targetCount,
int fromIndex) {
if (fromIndex >= sourceCount) {
return (targetCount == 0 ? sourceCount : -1);
}
if (fromIndex < 0) {
fromIndex = 0;
}
if (targetCount == 0) {
return fromIndex;
}
char first = target[targetOffset];
int max = sourceOffset + (sourceCount - targetCount);
for (int i = sourceOffset + fromIndex; i <= max; i++) {
/* Look for first character. */
if (source[i] != first) {
while (++i <= max && source[i] != first);
}
/* Found first character, now look at the rest of v2 */
if (i <= max) {
int j = i + 1;
int end = j + targetCount - 1;
for (int k = targetOffset + 1; j < end && source[j]
== target[k]; j++, k++);
if (j == end) {
/* Found whole string. */
return i - sourceOffset;
}
}
}
return -1;
}
這個是上面indexOf的一個過載,主要是實現找到某個子串在當前字串的起始位置,若沒找到,則返回-1。
大致說下這裡的實現思路:先是進行一系列的初始判定,比如子串長度不能大於當前字串。然後在當前字串中找到子串的第一個字元的位置 i ,從這個位置開始,和子串每一個字元比較。若完全匹配,則返回結果,如果在這個過程中,某個字元不匹配,則從 i+1 的位置開始繼續尋找子串第一個字元的位置,後繼續比較。
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 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);
}
concat的作用是將str拼接到當前字串後面,通過程式碼也可以看出其實就是建一個新的字串。
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;
}
替換操作,主要是將原來字串中的oldChar全部替換成newChar。看這裡實現,主要是先找到第一個所要替換的字串的位置 i ,將i之前的字元直接複製到一個新char陣列。然後從 i 開始再對每一個字元進行判斷是不是所要替換的字元。
public boolean matches(String regex) {
return Pattern.matches(regex, this);
}
public String replaceFirst(String regex, String replacement) {
return Pattern.compile(regex).matcher(this).replaceFirst(replacement);
}
public String replaceAll(String regex, String replacement) {
return Pattern.compile(regex).matcher(this).replaceAll(replacement);
}
public String replace(CharSequence target, CharSequence replacement) {
return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
this).replaceAll(Matcher.quoteReplacement(replacement.toString()));
}
這幾個方法都是使用了正則的方式來進行處理的。包括最後一個雖然引數不用提供正則規則,但內部其實也是使用了Pattern類的正則操作。
public String trim() {
int len = value.length;
int st = 0;
char[] val = value; /* avoid getfield opcode */
while ((st < len) && (val[st] <= ' ')) {
st++;
}
while ((st < len) && (val[len - 1] <= ' ')) {
len--;
}
return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
}
這個函式平時用的應該比較多,刪除字串前後的空格,原理是通過找出前後第一個不是空格的字串,返回原字串的該子串。
總結:
在String中,其實最底層的實現就是通過一個final char value[] 來儲存String字串的,抓住這一點,其實很多設計方法,方法的實現方式就顯而易見了。
演示程式
Strin建構函式
/**
* String 建構函式演示程式
*
* @author skywang
*/
import java.nio.charset.Charset;
import java.io.UnsupportedEncodingException;
public class StringContructorTest {
public static void main(String[] args) {
testStringConstructors() ;
}
/**
* String 建構函式測試程式
*/
private static void testStringConstructors() {
try {
System.out.println("-------------------------------- testStringConstructors -----------------------");
String str01 = new String();
String str02 = new String("String02");
String str03 = new String(new char[]{'s','t','r','0','3'});
String str04 = new String(new char[]{'s','t','r','0','4'}, 1, 3); // 1表示起始位置,3表示個數
String str05 = new String(new byte[]{0x61, 0x62, 0x63, 0x64, 0x65}); // 0x61在ASC表中,對應字元"a"; 1表示起始位置,3表示長度
String str06 = new String(new byte[]{0x61, 0x62, 0x63, 0x64, 0x65}, 1, 3); // 0x61在ASC表中,對應字元"a"; 1表示起始位置,3表示長度
String str07 = new String(new byte[]{0x61, 0x62, 0x63, 0x64, 0x65}, 0); // 0x61在ASC表中,對應字元"a";0,表示“高位元組”
String str08 = new String(new byte[]{0x61, 0x62, 0x63, 0x64, 0x65}, 0, 1, 3); // 0x61在ASC表中,對應字元"a"; 0,表示“高位元組”;1表示起始位置,3表示長度
String str09 = new String(new byte[]{(byte)0xe5, (byte)0xad, (byte)0x97, /* 字-對應的utf-8編碼 */
(byte)0xe7, (byte)0xac, (byte)0xa6, /* 符-對應的utf-8編碼 */
(byte)0xe7, (byte)0xbc, (byte)0x96, /* 編-對應的utf-8編碼 */
(byte)0xe7, (byte)0xa0, (byte)0x81, /* 碼-對應的utf-8編碼 */ },
0, 12, "utf-8"); // 0表示起始位置,12表示長度。
String str10 = new String(new byte[]{(byte)0x5b, (byte)0x57, /* 字-對應的utf-16編碼 */
(byte)0x7b, (byte)0x26, /* 符-對應的utf-16編碼 */
(byte)0x7f, (byte)0x16, /* 編-對應的utf-16編碼 */
(byte)0x78, (byte)0x01, /* 碼-對應的utf-16編碼 */ },
0, 8, "utf-16"); // 0表示起始位置,8表示長度。
String str11 = new String(new byte[]{(byte)0xd7, (byte)0xd6, /* 字-對應的gb2312編碼 */
(byte)0xb7, (byte)0xfb, /* 符-對應的gb2312編碼 */
(byte)0xb1, (byte)0xe0, /* 編-對應的gb2312編碼 */
(byte)0xc2, (byte)0xeb, /* 碼-對應的gb2312編碼 */ },
Charset.forName("gb2312"));
String str12 = new String(new byte[]{(byte)0xd7, (byte)0xd6, /* 字-對應的gbk編碼 */
(byte)0xb7, (byte)0xfb, /* 符-對應的gbk編碼 */
(byte)0xb1, (byte)0xe0, /* 編-對應的gbk編碼 */
(byte)0xc2, (byte)0xeb, /* 碼-對應的gbk編碼 */ },
0, 8, Charset.forName("gbk"));
String str13 = new String(new int[] {0x5b57, 0x7b26, 0x7f16, 0x7801}, 0, 4); // "字元編碼"(\u5b57是‘字’的unicode編碼)。0表示起始位置,4表示長度。
String str14 = new String(new StringBuffer("StringBuffer"));
String str15 = new String(new StringBuilder("StringBuilder"));
System.out.printf(" str01=%s \n str02=%s \n str03=%s \n str04=%s \n str05=%s \n str06=%s \n str07=%s \n str08=%s\n str09=%s\n str10=%s\n str11=%s\n str12=%s\n str13=%s\n str14=%s\n str15=%s\n",
str01, str02, str03, str04, str05, str06, str07, str08, str09, str10, str11, str12, str13, str14, str15);
System.out.println();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
結果:
-------------------------------- testStringConstructors ----------------------- str01= str02=String02 str03=str03 str04=tr0 str05=abcde str06=bcd str07=abcde str08=bcd str09=字元編碼 str10=字元編碼 str11=字元編碼 str12=字元編碼 str13=字元編碼 str14=StringBuffer str15=StringBuilder
將各種物件轉換為String物件的API
/**
* String value相關示例
*
* @author skywang
*/
import java.util.HashMap;
public class StringValueTest {
public static void main(String[] args) {
testValueAPIs() ;
}
/**
* String 的valueOf()演示程式
*/
private static void testValueAPIs() {
System.out.println("-------------------------------- testValueAPIs --------------------------------");
// 1. String valueOf(Object obj)
// 實際上,返回的是obj.toString();
HashMap map = new HashMap();
map.put("1", "one");
map.put("2", "two");
map.put("3", "three");
System.out.printf("%-50s = %s\n", "String.valueOf(map)", String.valueOf(map));
// 2.String valueOf(boolean b)
System.out.printf("%-50s = %s\n", "String.valueOf(true)", String.valueOf(true));
// 3.String valueOf(char c)
System.out.printf("%-50s = %s\n", "String.valueOf('m')", String.valueOf('m'));
// 4.String valueOf(int i)
System.out.printf("%-50s = %s\n", "String.valueOf(96)", String.valueOf(96));
// 5.String valueOf(long l)
System.out.printf("%-50s = %s\n", "String.valueOf(12345L)", String.valueOf(12345L));
// 6.String valueOf(float f)
System.out.printf("%-50s = %s\n", "String.valueOf(1.414f)", String.valueOf(1.414f));
// 7.String valueOf(double d)
System.out.printf("%-50s = %s\n", "String.valueOf(3.14159d)", String.valueOf(3.14159d));
// 8.String valueOf(char[] data)
System.out.printf("%-50s = %s\n", "String.valueOf(new char[]{'s','k','y'})", String.valueOf(new char[]{'s','k','y'}));
// 9.String valueOf(char[] data, int offset, int count)
System.out.printf("%-50s = %s\n", "String.valueOf(new char[]{'s','k','y'}, 0, 2)", String.valueOf(new char[]{'s','k','y'}, 0, 2));
System.out.println();
}
}
主要是ValueOf方法
結果:
-------------------------------- testValueAPIs -------------------------------- String.valueOf(map) = {3=three, 2=two, 1=one} String.valueOf(true) = true String.valueOf('m') = m String.valueOf(96) = 96 String.valueOf(12345L) = 12345 String.valueOf(1.414f) = 1.414 String.valueOf(3.14159d) = 3.14159 String.valueOf(new char[]{'s','k','y'}) = sky String.valueOf(new char[]{'s','k','y'}, 0, 2) = sk
String中index相關的API
/**
* String 中index相關API演示
*
* @author skywang
*/
public class StringIndexTest {
public static void main(String[] args) {
testIndexAPIs() ;
}
/**
* String 中index相關API演示
*/
private static void testIndexAPIs() {
System.out.println("-------------------------------- testIndexAPIs --------------------------------");
String istr = "abcAbcABCabCaBcAbCaBCabc";
System.out.printf("istr=%s\n", istr);
// 1. 從前往後,找出‘a’第一次出現的位置
System.out.printf("%-30s = %d\n", "istr.indexOf((int)'a')", istr.indexOf((int)'a'));
// 2. 從位置5開始,從前往後,找出‘a’第一次出現的位置
System.out.printf("%-30s = %d\n", "istr.indexOf((int)'a', 5)", istr.indexOf((int)'a', 5));
// 3. 從後往前,找出‘a’第一次出現的位置
System.out.printf("%-30s = %d\n", "istr.lastIndexOf((int)'a')", istr.lastIndexOf((int)'a'));
// 4. 從位置10開始,從後往前,找出‘a’第一次出現的位置
System.out.printf("%-30s = %d\n", "istr.lastIndexOf((int)'a', 10)", istr.lastIndexOf((int)'a', 10));
// 5. 從前往後,找出"bc"第一次出現的位置
System.out.printf("%-30s = %d\n", "istr.indexOf(\"bc\")", istr.indexOf("bc"));
// 6. 從位置5開始,從前往後,找出"bc"第一次出現的位置
System.out.printf("%-30s = %d\n", "istr.indexOf(\"bc\", 5)", istr.indexOf("bc", 5));
// 7. 從後往前,找出"bc"第一次出現的位置
System.out.printf("%-30s = %d\n", "istr.lastIndexOf(\"bc\")", istr.lastIndexOf("bc"));
// 8. 從位置4開始,從後往前,找出"bc"第一次出現的位置
System.out.printf("%-30s = %d\n", "istr.lastIndexOf(\"bc\", 4)", istr.lastIndexOf("bc", 4));
System.out.println();
}
}
結果:
-------------------------------- testIndexAPIs -------------------------------- istr=abcAbcABCabCaBcAbCaBCabc istr.indexOf((int)'a') = 0 istr.indexOf((int)'a', 5) = 9 istr.lastIndexOf((int)'a') = 21 istr.lastIndexOf((int)'a', 10) = 9 istr.indexOf("bc") = 1 istr.indexOf("bc", 5) = 22 istr.lastIndexOf("bc") = 22 istr.lastIndexOf("bc", 4) = 4
String中用於比較的API
/**
* String 中比較相關API演示
*
* @author skywang
*/
public class StringCompareTest {
public static void main(String[] args) {
testCompareAPIs() ;
}
/**
* String 中比較相關API演示
*/
private static void testCompareAPIs() {
System.out.println("-------------------------------- testCompareAPIs ------------------------------");
//String str = "abcdefghijklmnopqrstuvwxyz";
String str = "abcAbcABCabCAbCabc";
System.out.printf("str=%s\n", str);
// 1. 比較“2個String是否相等”
System.out.printf("%-50s = %b\n",
"str.equals(\"abcAbcABCabCAbCabc\")",
str.equals("abcAbcABCabCAbCabc"));
// 2. 比較“2個String是否相等(忽略大小寫)”
System.out.printf("%-50s = %b\n",
"str.equalsIgnoreCase(\"ABCABCABCABCABCABC\")",
str.equalsIgnoreCase("ABCABCABCABCABCABC"));
// 3. 比較“2個String的大小”
System.out.printf("%-40s = %d\n", "str.compareTo(\"abce\")", str.compareTo("abce"));
// 4. 比較“2個String的大小(忽略大小寫)”
System.out.printf("%-40s = %d\n", "str.compareToIgnoreCase(\"ABC\")", str.compareToIgnoreCase("ABC"));
// 5. 字串的開頭是不是"ab"
System.out.printf("%-40s = %b\n", "str.startsWith(\"ab\")", str.startsWith("ab"));
// 6. 字串的從位置3開頭是不是"ab"
System.out.printf("%-40s = %b\n", "str.startsWith(\"Ab\")", str.startsWith("Ab", 3));
// 7. 字串的結尾是不是"bc"
System.out.printf("%-40s = %b\n", "str.endsWith(\"bc\")", str.endsWith("bc"));
// 8. 字串的是不是包含"ABC"
System.out.printf("%-40s = %b\n", "str.contains(\"ABC\")", str.contains("ABC"));
// 9. 比較2個字串的部分內容
String region1 = str.substring(2, str.length()); // 獲取str位置3(包括)到末尾(不包括)的子字串
// 將“str中從位置2開始的字串”和“region1中位置0開始的字串”進行比較,比較長度是5。
System.out.printf("regionMatches(%s) = %b\n", region1,
str.regionMatches(2, region1, 0, 5));
// 10. 比較2個字串的部分內容(忽略大小寫)
String region2 = region1.toUpperCase(); // 將region1轉換為大寫
String region3 = region1.toLowerCase(); // 將region1轉換為小寫
System.out.printf("regionMatches(%s) = %b\n", region2,
str.regionMatches(2, region2, 0, 5));
System.out.printf("regionMatches(%s) = %b\n", region3,
str.regionMatches(2, region3, 0, 5));
// 11. 比較“String”和“StringBuffer”的內容是否相等
System.out.printf("%-60s = %b\n",
"str.contentEquals(new StringBuffer(\"abcAbcABCabCAbCabc\"))",
str.contentEquals(new StringBuffer("abcAbcABCabCAbCabc")));
// 12. 比較“String”和“StringBuilder”的內容是否相等
System.out.printf("%-60s = %b\n",
"str.contentEquals(new StringBuilder(\"abcAbcABCabCAbCabc\"))",
str.contentEquals(new StringBuilder("abcAbcABCabCAbCabc")));
// 13. match()測試程式
// 正則表示式 xxx.xxx.xxx.xxx,其中xxx中x的取值可以是0~9,xxx中有1~3位。
String reg_ipv4 = "[0-9]{3}(\\.[0-9]{1,3}){3}";
String ipv4addr1 = "192.168.1.102";
String ipv4addr2 = "192.168";
System.out.printf("%-40s = %b\n", "ipv4addr1.matches()", ipv4addr1.matches(reg_ipv4));
System.out.printf("%-40s = %b\n", "ipv4addr2.matches()", ipv4addr2.matches(reg_ipv4));
System.out.println();
}
}
結果:
-------------------------------- testCompareAPIs ------------------------------ str=abcAbcABCabCAbCabc str.equals("abcAbcABCabCAbCabc") = true str.equalsIgnoreCase("ABCABCABCABCABCABC") = true str.compareTo("abce") = -36 str.compareToIgnoreCase("ABC") = 15 str.startsWith("ab") = true str.startsWith("Ab") = true str.endsWith("bc") = true str.contains("ABC") = true regionMatches(cAbcABCabCAbCabc) = true regionMatches(CABCABCABCABCABC) = false regionMatches(cabcabcabcabcabc) = false str.contentEquals(new StringBuffer("abcAbcABCabCAbCabc")) = true str.contentEquals(new StringBuilder("abcAbcABCabCAbCabc")) = true ipv4addr1.matches() = true ipv4addr2.matches() = false
String中修改的API
/**
* String 中 修改(追加/替換/擷取/分割)字串的相關API演示
*
* @author skywang
*/
public class StringModifyTest {
public static void main(String[] args) {
testModifyAPIs() ;
}
/**
* String 中 修改(追加/替換/擷取/分割)字串的相關API演示
*/
private static void testModifyAPIs() {
System.out.println("-------------------------------- testModifyAPIs -------------------------------");
String str = " abcAbcABCabCAbCabc ";
System.out.printf("str=%s, len=%d\n", str, str.length());
// 1.追加
// 將"123"追加到str之後
System.out.printf("%-30s = %s\n", "str.concat(\"123\")",
str.concat("123"));
// 2.擷取
// 擷取str中從位置7(包括)開始的元素。
System.out.printf("%-30s = %s\n", "str.substring(7)", str.substring(7));
// 擷取str中從位置7(包括)到位置10(不包括)之間的元素。
System.out.printf("%-30s = %s\n", "str.substring(7, 10)", str.substring(7, 10));
// 刪除str中首位的空格,並返回。
System.out.printf("%-30s = %s, len=%d\n", "str.trim()", str.trim(), str.trim().length());
// 3.替換
// 將str中的 “字元‘a’” 全部替換為 “字元‘_’”
System.out.printf("%-30s = %s\n", "str.replace('a', 'M')", str.replace('a', '_'));
// 將str中的第一次出現的“字串“a”” 替換為 “字串“###””
System.out.printf("%-30s = %s\n", "str.replaceFirst(\"a\", \"###\")", str.replaceFirst("a", "###"));
// 將str中的 “字串“a”” 全部替換為 “字串“$$$””
System.out.printf("%-30s = %s\n", "str.replace(\"a\", \"$$$\")", str.replace("a", "$$$"));
// 4.分割
// 以“b”作為分隔符,對str進行分割
String[] splits = str.split("b");
for (int i=0; i<splits.length; i++) {
System.out.printf("splits[%d]=%s\n", i, splits[i]);
}
System.out.println();
}
}
結果:
-------------------------------- testModifyAPIs ------------------------------- str= abcAbcABCabCAbCabc , len=20 str.concat("123") = abcAbcABCabCAbCabc 123 str.substring(7) = ABCabCAbCabc str.substring(7, 10) = ABC str.trim() = abcAbcABCabCAbCabc, len=18 str.replace('a', 'M') = _bcAbcABC_bCAbC_bc str.replaceFirst("a", "###") = ###bcAbcABCabCAbCabc str.replace("a", "$$$") = $$$bcAbcABC$$$bCAbC$$$bc splits[0]= a splits[1]=cA splits[2]=cABCa splits[3]=CA splits[4]=Ca splits[5]=c
其他API
/**
* String 中其它的API
*
* @author skywang
*/
public class StringOtherTest {
public static void main(String[] args) {
testOtherAPIs() ;
}
/**
* String 中其它的API
*/
private static void testOtherAPIs() {
System.out.println("-------------------------------- testOtherAPIs --------------------------------");
String str = "0123456789";
System.out.printf("str=%s\n", str);
// 1. 字串長度
System.out.printf("%s = %d\n", "str.length()", str.length());
// 2. 字串是否為空
System.out.printf("%s = %b\n", "str.isEmpty()", str.isEmpty());
// 3. [位元組] 獲取字串對應的位元組陣列
byte[] barr = str.getBytes();
for (int i=0; i<barr.length; i++) {
System.out.printf("barr[%d]=0x%x ", i, barr[i]);
}
System.out.println();
// 4. [字元] 獲取字串位置4的字元
System.out.printf("%s = %c\n", "str.charAt(4)", str.charAt(4));
// 5. [字元] 獲取字串對應的字元陣列
char[] carr = str.toCharArray();
for (int i=0; i<carr.length; i++) {
System.out.printf("carr[%d]=%c ", i, carr[i]);
}
System.out.println();
// 6. [字元] 獲取字串中部分元素對應的字元陣列
char[] carr2 = new char[3];
str.getChars(6, 9, carr2, 0);
for (int i=0; i<carr2.length; i++) {
System.out.printf("carr2[%d]=%c ", i, carr2[i]);
}
System.out.println();
// 7. [字元] 獲取字元陣列對應的字串
System.out.printf("%s = %s\n",
"str.copyValueOf(new char[]{'a','b','c','d','e'})",
String.copyValueOf(new char[]{'a','b','c','d','e'}));
// 8. [字元] 獲取字元陣列中部分元素對應的字串
System.out.printf("%s = %s\n",
"str.copyValueOf(new char[]{'a','b','c','d','e'}, 1, 4)",
String.copyValueOf(new char[]{'a','b','c','d','e'}, 1, 4));
// 9. format()示例,將物件陣列按指定格式轉換為字串
System.out.printf("%s = %s\n",
"str.format()",
String.format("%s-%d-%b", "abc", 3, true));
System.out.println();
}
}
結果:
------------------------------- testOtherAPIs -------------------------------- str=0123456789 str.length() = 10 str.isEmpty() = false barr[0]=0x30 barr[1]=0x31 barr[2]=0x32 barr[3]=0x33 barr[4]=0x34 barr[5]=0x35 barr[6]=0x36 barr[7]=0x37 barr[8]=0x38 barr[9]=0x39 str.charAt(4) = 4 carr[0]=0 carr[1]=1 carr[2]=2 carr[3]=3 carr[4]=4 carr[5]=5 carr[6]=6 carr[7]=7 carr[8]=8 carr[9]=9 carr2[0]=6 carr2[1]=7 carr2[2]=8 str.copyValueOf(new char[]{'a','b','c','d','e'}) = abcde str.copyValueOf(new char[]{'a','b','c','d','e'}, 1, 4) = bcde str.format() = abc-3-true