1.從概念上講,java字符串就是Unicode字符串。
2.字符串拼接
用指定分隔符拼接字符串數組時,使用StringJoiner或者String.join()更方便;
用StringJoiner拼接字符串時,還可以額外附加一個“開頭”和“結尾”。
@Test
public void join1() {
String[] names = {"Bob", "Alice", "Grace"};
StringJoiner sj = new StringJoiner(", ", "Hello ", "!");
for (String name : names) {
sj.add(name);
}
System.out.println(sj.toString());
}
@Test
public void join2() {
String[] names = {"Bob", "Alice", "Grace"};
String s = String.join(", ", names);
System.out.println(s);
}
3.子串substring
String類的substring方法可以從一個較大的字符串提取出一個子串,
public static void main(String[] args) {
//子串
String s = "你好,今天是2017年12月24日,聖誕快樂!!";
String date = s.substring(6, 17);
System.out.println(date);//輸出:2017年12月24日
}
查看String源碼,substring(beginIndex,endIndex):返回子字符串,從beginIndex到endIndex-1,通過調用String構造方法創建了一個新的String對象。
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);
}
private final char value[];
public String(char value[], int offset, int count) {
if (offset < 0) {
throw new StringIndexOutOfBoundsException(offset);
}
if (count <= 0) {
if (count < 0) {
throw new StringIndexOutOfBoundsException(count);
}
if (offset <= value.length) {
this.value = "".value;
return;
}
}
// Note: offset or count might be near -1>>>1.
if (offset > value.length - count) {
throw new StringIndexOutOfBoundsException(offset + count);
}
this.value = Arrays.copyOfRange(value, offset, offset+count);
}
4.字符串判等equals
當創建 String 類型的對象時,虛擬機會在常量池中查找有沒有已經存在的值和要創建的值相同的對象,如果有就把它賦給當前引用。如果沒有就在常量池中重新創建一個 String 對象。所以一定不要使用運算符檢測兩個字符串是否相等!這個運算符只能夠確定兩個字符串是否放置在同一個位置上。如果虛擬機始終將相同的字符串共享,就可以使用運算符檢測是否相等。但實際上只有字符串常量是共享的,而+或substring等操作產生的結果並不是共享的。
另外,使用equals方法時要注意避免NP。
public static void main(String[] args) {
//判等
String s3 = "abc";
String s4 = "abc";
String s5 = new String("abc");
String s6 = new String("abc");
System.out.println(s3 == s4);//true
System.out.println(s3.equals(s4));//true
System.out.println(s5 == s6);//false
System.out.println(s5.equals(s6));//true
//避免NP
String s7 = null;
// System.out.println(s7.equals("null"));//NP
System.out.println(s7 != null && s7.equals("null"));
System.out.println("null".equals(s7));
}
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;
}
5.String,StringBuilder和StringBuffer
①String字符串爲什麼是不可變的?
簡單的來說:String 類中使用 final 關鍵字修飾字符數組來保存字符串,所以 String 對象是不可變的。
低版本實現:
private final char value[];
Java9之後的版本:
private final byte[] value;
而 StringBuilder 與 StringBuffer 都繼承自 AbstractStringBuilder 類,在 AbstractStringBuilder 中也是使用字符數組保存字符串byte[] value, 沒有用 final 關鍵字修飾,所以這兩種對象都是可變的。
②線程安全性
String 中的對象是不可變的,也就可以理解爲常量,線程安全。AbstractStringBuilder 是 StringBuilder 與 StringBuffer 的公共父類,定義了一些字符串的基本操作。StringBuffer 對方法加了同步鎖或者對調用的方法加了同步鎖,所以是線程安全的。StringBuilder 並沒有對方法進行加同步鎖,所以是非線程安全的。
@Override
@HotSpotIntrinsicCandidate
public StringBuilder append(String str) {
super.append(str);
return this;
}
@Override
@HotSpotIntrinsicCandidate
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
③對於三者使用的總結:
- 少量操作字符串用 String
- 單線程操作字符串緩衝區下大量數據用 StringBuilder
- 多線程操作字符串緩衝區下大量數據用 StringBuffer