個人吐槽:對于這章,第一遍讀和重讀之后果然看法不同:第一次讀時覺得,文章用了大篇幅來介紹 API,這樣我可以比較容易理解這個方法是干嘛的,以此理解這個類是干嘛的;但是重讀之后覺得,文章居然用了這么大篇幅在講 API(尤其是正則表達(dá)式),如果我需要知道具體某個方法怎么用,直接去官方文檔查多好啊,這種情況下,我更希望得到某個方法可能出現(xiàn)的坑,而不只是方法的介紹(畢竟,這往往需要踩過坑之后總結(jié)的,hhh)。
1. String 基礎(chǔ)
1.1 String 不可變
String 對象是不可變的。(只讀性)
String 類中每一個修改 String 值的方法,實(shí)際上都是創(chuàng)建了一個新的 String 對象,原有的 String 對象絲毫未動。
1.2 String 的連接問題
"+" 和 "+=" 是經(jīng)過 Java 重載之后的兩個操作符,可用來對 String 拼接。
同時 Java 不允許程序員重載任何操作符。
String + String 與 StringBuilder.append() 對比
直接相加字符串(不含 String 引用,形如"hello"+"world"):效率最高,簡單來說,其全部為常量,編譯期就會優(yōu)化為 "helloworld",編譯期值就已經(jīng)確定了。
間接相加字符串(含 String 引用,如 str + "world"):效率最差,簡單來說會取出原有 String 與 "world" 進(jìn)行拼接,然后將結(jié)果存入另一個新的 String 對象中,雖然編譯器進(jìn)行了優(yōu)化,通過 StringBuilder.append() 來實(shí)現(xiàn)拼接,但是當(dāng)出現(xiàn)循環(huán)時,會出現(xiàn) StringBuilder 對象的反復(fù)生成。
-
StringBuilder.append():介于上述二者之間。
綜上:
- 如果是固定字符串如“xxx”,那么直接用 "+" 即可。
- 如果間接相加字符串,但是次數(shù)很少,則 StringBuilder 和 "+" 都可,差別不很大
- 如果簡介相加字符串,且多次循環(huán),那么強(qiáng)烈建議 StringBuilder
1.3 無意識的遞歸
當(dāng)想要打印出某對象的內(nèi)存地址時,應(yīng)該調(diào)用 Object.toString() / super.toString() 方法,而非 this 上的 toString()。
換句話說,就是當(dāng)前對象重寫的 toString() 方法中,應(yīng)該調(diào)用 super.toString() 而不是使用 this。
在當(dāng)前對象的 toString() 方法中,如果調(diào)用 this,會發(fā)生自動類型轉(zhuǎn)換,將對象類型轉(zhuǎn)換為 String 類型。那么怎么轉(zhuǎn)換呢?正式通過調(diào)用 this (當(dāng)前對象)上的 toString() 方法。
em...完美的形成一個遞歸,自己調(diào)用自己,且沒有終止條件。
1.4 String API
這里有想知道的一切 API
https://docs.oracle.com/javase/9/docs/api/java/lang/String.html
1.5 格式化輸出
如下幾種方式:
System.out.println() 強(qiáng)行湊成指定形式,不推薦
System.out.format():針對于 PrintStream 和 PrintWriter,其中包括 System.out 對象
-
Formatter 類:可以看作是一個翻譯器,將格式化字符串與數(shù)據(jù)翻譯成需要的結(jié)果。
-
格式化說明符:%[argument_index$][flags][width][.precision]conversion
width 指定域的最小尺寸
precision 指定最大尺寸,適用于 String 和浮點(diǎn)數(shù),無法用于整數(shù)
-
常用的類型轉(zhuǎn)換
類型轉(zhuǎn)換字符 意義 類型轉(zhuǎn)換字符 意義 d 十進(jìn)制整數(shù)型 e 科學(xué)計數(shù)浮點(diǎn)數(shù) c Unicode 字符 x 十六進(jìn)制整數(shù) b Boolean 值 h 十六進(jìn)制散列碼 s String % 字符 "%" f 十進(jìn)制浮點(diǎn)數(shù) 更多詳見 https://docs.oracle.com/javase/9/docs/api/java/util/Formatter.html
-
-
String.format()
- 實(shí)際上就是內(nèi)部封裝了 Formatter 對象,然后將傳入的參數(shù)傳遞給 Formatter,由它來進(jìn)行具體的處理。
2. 正則表達(dá)式
2.1 通用正則表達(dá)式
就不整理出來了,放上一篇教程
http://www.runoob.com/regexp/regexp-tutorial.html
https://docs.oracle.com/javase/9/docs/api/java/util/regex/Pattern.html
補(bǔ)充一點(diǎn):
- 在 java 中,"\\" 表示一個有效的 "\",因此如果需要表示正則表達(dá)式里的一個數(shù)字 "\d",需要寫成 "\\d"
2.2 Pattern & Matcher
接口 CharSequence 從 CharBuffer、String、String Buffer、StringBuilder 類之中抽象出了字符序列的一般化定義:
interface CharSequence { charAt(int i); length(); subSequence(int start,int end); toString(); }
本小節(jié)簡單介紹 Java 中如何處理正則表達(dá)式:
2.2.1 基礎(chǔ)使用:
step 1:Pattern.compile(regex) 編譯 String 類型的 regex,并產(chǎn)生 Pattern 對象
step 2:Pattern.matcher(待檢索的字符串)生成一個 Matcher 對象
直接上例子,通過例子說明:
import java.util.regex;
public class TestRegularExpression {
public static void main(String[] args) {
String[] array = {"aabbcc", "aab", "aab+", "(b+)"};
for (String arg : array) {
System.out.println();
print("Regular expression: \"" + arg + "\"");
Pattern p = Pattern.compile(arg); // step1: Pattern 表示編譯后的匹配模型Pattern.(編譯后的正則表達(dá)式)
Matcher m = p.matcher("aabbcc"); // step2: 模型實(shí)例 檢索 待匹配字符串并 生成一個匹配對象Matcher, Matcher有很多方法
while (m.find()) {
print("Match \"" + m.group() // 待匹配的字符串
+ "\" at positions "
+ m.start() // 字符串匹配regex的起始位置
+ "-" + (m.end() - 1)); // 字符串匹配regex的終點(diǎn)位置
}
}
}
Pattern 對象表示編譯后的正則表達(dá)式,重點(diǎn)在于 Matcher 對象,它提供了一系列方法來進(jìn)行正則的匹配,下面簡單介紹:
詳見 https://docs.oracle.com/javase/9/docs/api/overview-summary.html
Matcher.find():用來在 CharSequence 查找多個匹配
-
Matcher.group():用來獲取與組相關(guān)的信息
組是用括號劃分的正則表達(dá)式,可以用組的編號來引用某個組,組0表示整個表達(dá)式,組1表示被第一隊(duì)括號括起來的組。。。
A(B(C))D : 組0是 ABCD,組1是B,組2是C
Matcher.start() & end():返回先前匹配的起始和截止位置的索引。
-
Pattern 標(biāo)記:重載方法,Pattern.compile(String regex, int flags) 接受一個 flags 參數(shù),來調(diào)整匹配的行為。具體看官方文檔:
https://docs.oracle.com/javase/9/docs/api/java/util/regex/Pattern.html
split():將輸入字符串?dāng)嚅_成字符串對象數(shù)組。
替換 replacexxx():替換文本
reset():將現(xiàn)有的 Matcher 對象應(yīng)用于一個新的字符序列。
3. 掃描輸入
Scanner 是 Java SE5 中添加的特性,主要就是減輕掃描輸入的工作負(fù)擔(dān),最實(shí)用的是獲取控制臺輸入,其他比如從文件讀取內(nèi)容的,emmm...感覺有些雞肋。
舉個使用的簡單例子:
public class SimpleScanner {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
System.out.println("請輸入字符串:");
while (true) {
String line = s.nextLine();
if (line.equals("exit")) break;
System.out.println(">>>" + line);
}
}
}
/*
請輸入字符串:
whdalive
>>>whdalive
exit
Process finished with exit code 0
*/
Scanner 使用很方便,這是因?yàn)樗臉?gòu)造器可以接受任何類型的輸入對象,有了 Scanner 之后,所有輸入、分詞以及翻譯的操作都隱藏在不同類型的 next 方法中。
3.1 Scanner 定界符
默認(rèn)情況下,Scanner 根據(jù)空白字符對輸入進(jìn)行分詞,但是我們可以用正則表達(dá)式指定自己所需的定界符。
public static void main(String[] args) throws FileNotFoundException {
Scanner s = new Scanner("12,42,78,99,42");
s.useDelimiter(",");
while (s.hasNext()) {
System.out.println(s.next());
}
}
/*輸出
12
42
78
99
42
*/
我們通過 useDelimiter() 方法來指定定界符,顯然上述代碼中我們使用的是逗號。
3.2 正則表達(dá)式掃描
Scanner 的 next 方法中,有一個重載方法可以接收 String 的正則表達(dá)式,此時它會找到下一個匹配該模式的輸入部分,然后調(diào)用 match() 方法就可以獲得匹配的結(jié)果。工作方式和正則表達(dá)式匹配是類似的。
但是需要注意一點(diǎn):它僅僅針對下一個輸入分詞進(jìn)行匹配,如果正則表達(dá)式中有定界符,那么永遠(yuǎn)不可能匹配成功。