下面說一些平時可能不太注意的知識點(diǎn):
String 對象具有只讀特性,所以指向它的任何引用都不可能改變它的值,因此不會其它的引用有什么影響。String的"+"與"+="是java中僅有的兩年重載過的操作符,而java并不允許程序員重載任何操作符。
String的優(yōu)化
由于String的不可改變的特性,每次改變都會生成一個新的String對象。那如果我們會使用String的重載運(yùn)算符"+"拼接字符串時不是會生成大量的中間對象?java設(shè)計(jì)師一開始也是這樣做的(這也是軟件設(shè)計(jì)中的一個教訓(xùn):除非你用代碼將系統(tǒng)實(shí)現(xiàn),并將它動起來,否則你無法真正了解它會有什么問題),然后他們發(fā)現(xiàn)其性能相當(dāng)糟糕。
public class Concatenation {
public static void main(String[] args){
String mango="mango";
String s="abc"+mango+"def"+45;
System.out.println(s);
}
}
然后在class生成目錄下使用javap -c Concatenation
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String mango
2: astore_1
3: new #3 // class java/lang/StringBuilder
6: dup
7: invokespecial #4 // Method java/lang/StringBuilder."<
init>":()V
10: ldc #5 // String abc
12: invokevirtual #6 // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: aload_1
16: invokevirtual #6 // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: ldc #7 // String def
21: invokevirtual #6 // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: bipush 45
26: invokevirtual #8 // Method java/lang/StringBuilder.ap
pend:(I)Ljava/lang/StringBuilder;
29: invokevirtual #9 // Method java/lang/StringBuilder.to
String:()Ljava/lang/String;
32: astore_2
33: getstatic #10 // Field java/lang/System.out:Ljava/
io/PrintStream;
36: aload_2
37: invokevirtual #11 // Method java/io/PrintStream.printl
n:(Ljava/lang/String;)V
40: return
}
其中dup與invokevirtural語句相當(dāng)于java虛擬機(jī)上的匯編語言。從上可以看出編譯器自動引用了StringBuilder類,使得那句代碼更高效。但是編譯器為我們優(yōu)化的程序有限。隨時可以用javap來分析你的程序。
當(dāng)你為一個類重寫toString時如果字符串比較簡單那么可以依賴編譯器,否則使用StringBuilder更高效。
3.無意識的遞歸:當(dāng)重寫一個類的的toString的時候,如果你希望打印對象的內(nèi)存地址不要使用this關(guān)鍵字,比如:
@Override
public String toString() {
return "*** address:"+this;
}
當(dāng)調(diào)用this的時候又會調(diào)用這個方法的toString方法,所以在這里我們應(yīng)該使用super.toString()的方法。
- String的matches方法可以進(jìn)行正則表達(dá)式判斷:
System.out.println("-3478".matches("-?\\d+"));
輸出結(jié)果:
true
一般說來,比起功能有限的String類,我們更愿意使用Pattern和Matcher對象。
- Scanner定界符
在默認(rèn)情況下,Scanner根據(jù)空白字符對輸入進(jìn)行分詞,但是你可以用正則表達(dá)式指定自己所需的定界符。
public class ScannerDelimiter {
public static void main(String[] args) {
Scanner scanner = new Scanner("23,5,6,78,23");
scanner.useDelimiter("\\s*,\\s*");
while (scanner.hasNext()) {
System.out.println(scanner.nextInt());
}
}
}
運(yùn)行結(jié)果:
23
5
6
78
23
在java引入正則表達(dá)式和Scanner類之前,分割字符串的唯一方法是使用StringTokenier來分詞。不過,現(xiàn)在有了正則表式和Scanner,我們可以使用更加簡單,更加簡潔的方式來完成同樣的工作了。基本上StringTokenizer已經(jīng)棄用了。