Java SE 提供官方 API,而 API 由許多類別組成,包在套件給其他檔案引用,常見套件像是 java.util
、java.math
、java.lang
,分別有 Scanner
、BigDecimal
、String
。
java.util.Scanner
標準輸入
public class GuessNumber {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int answer = (int)(Math.random() * 10);
int guessNumber = -1;
do {
System.out.print("Choose a number: ");
guessNumber = sc.nextInt();
} while (answer != guessNumber);
System.out.print("Bingo");
}
}
java.math.BigDecimal
高精度浮點數計算
public class SumOfNumbers() {
public static void main(String[] args) {
BigDecimal num1 = new BigDecimal("34.56789876");
BigDecimal num2 = new BigDecimal("34.2234");
BigDecimal num3 = number1.add(number2);
System.out.println(num3);
}
}
java.lang.String 字串類別
字串是預設引入類別,宣告字串時不用輸入 import java.lang.String
就能引入套件。
不同於 BigDecimal、Scanner 的參考物件,String 不用構造函數就能將值指定給變數,因此這邊的值稱作 String literal(字串常值)。
// "text" 稱作 String literal
String str = "text";
"text" v.s new String("text")
既然有 String str = "text";
的簡便寫法,為什麼還要 new String("text")?
字串有不可改變的特性(Immutable)(字串是字元陣列組成),一旦變數給定字串值,值就不能改變。
// 編輯字串這動作,看起來像編輯同物件,編輯後字串其實記憶體指向的是另個物件
// str3 ≠ str2 ≠ str1
public class StrHandler {
public static void main(String[] args) {
String str1 = "It's ";
String str2 = "A";
String str3 = str1 + str2;
}
}
上面程式執行時,Java 記憶體配置先後產生字串物件「It's 」、「A」,再透過運算產生新字串「It's A」。整個過程產生了三個字串,但最後面只留「It's A」,剩下的「It's 」、「A」會透過 JVM 的垃圾回收機制(Garbage Collection,縮寫 GC)處理掉。
字串使用次數極高,為減少重複耗用的資源,也有所謂的字串池(String Pool)機制。
上面例子 StrHandler
類別適用沒有字串池的記憶體配置情況,有了字串池機制,JVM 會在 Heap 中開設一塊區域給字串池,並允許這些字串(準確來說是字串常值)被重複存取。
public class StrHandler {
public static void main(String[] args) {
String str1 = "A";
String str2 = new String("A");
String str3 = "A";
}
}
改寫後的 StrHandler
記憶體配置是這樣:
字串池的機制是,Java 會先判斷是否字串值,是的話先去字串池找有無相同字串,有就指向該字串位址,沒有則將它增加進去。而 new String()
建立的字串在底層構建時就直接視為另個物件,並放到字串池外的 Heap 區域。因此 str1 與 str3 的位址相同,與 str2 的位址不同。
public class StrHandler2 {
public static void main(String[] args) {
String str1 = "A"+"A";
String str2 = "AA";
String str3 = new String("AA");
System.out.println("A: " + (str1 == str2));
System.out.println("B: " + (str1.equals(str2)));
System.out.println("C: " + (str1 == str3));
System.out.println("D: " + (str1.equals(str3)));
}
}
以 StrHandler2 類別來小結一下,A~D 分別是:
A: true(==:比較參考位址)
B: true(.equal():比較物件儲存在 Heap 上的值)
C: false
D: true
A: str1 "A"+"A" 加總以後等於 "AA",執行後先去字串池找 "AA",得到 "AA" 的位址,str2 同理,只差在 str1 多了個加總的動作,str1 跟 str2 同樣指向字串池的位址 → 相同 reference
B: 對應同個字串池的字串 → 相同 value
C: str1 對應字串池裡的 "AA",str3 對應存在 Heap 裡面但在字串池外的 "AA" → 不同 reference
D: 一樣都叫做 "AA" → 相同 value
StringBuffer, StringBuilder
String 不可改變的特性,儘管有字串池的機制,將 str1 重新賦值 "AB" 的話,仍然會建立一個新物件,重複耗用資源的情況還是在,StringBuffer / StringBuilder 即是改善耗用資源的情形。
StringBuffer
StringBuffer 屬於同步類別(同步概念到線程再討論),保障線程的安全性,適合多線程場景執行。具備可改變的特性(mutable),只要建立一個 StringBuffer 就能做到建立多個 String 連接字串的工作,會是大量連接字串時,選用的類別之一(這裡以 .append()
方法實作),但要留意因有同步特性,如果在不需要多線程複雜性的單線程場景使用,會有資源耗用且比較慢的情況出現。
public class StringBufferHandler {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer("TEXT");
sb.append(" 123");
System.out.println(sb); // TEXT 123
}
}
StringBuilder
比 StringBuffer 晚一點,從 JDK 1.5 版本開始出現,功能類似於 StringBuffer,為的是解決同步類別的缺點,裡面定義的方法跟 StringBuffer 一樣,但適合單線程場景使用,不保障線程安全;實作上如果不擔心多線程的問題,直接用 StringBuilder 速度比較快。
public class StringBuilderHandler {
// 輸入一個句子,回傳句子倒著念的版本
public String reverseWordsInSentence(String sentence) {
if(sentence == null) return "INVALID";
if(sentence.equals("")) return "";
String[] words = sentence.split(" ");
StringBuilder reversedSentence = new StringBuilder();
for(int i=0; i<words.length; i++) {
StringBuilder sb = new StringBuilder(words[i]);
String reversedWord = sb.reverse().toString();
reversedSentence.append(reversedWord);
if(i<words.length-1) {
reversedSentence.append(" ");
}
}
return reversedSentence.toString();
}
}
常用靜態函數
假設 str 為字串變數:
回傳 int
取得指定位置字元:str.charAt([index])
取得指定字元順序(首個找到):str.indexof(char)
取得指定字元順序(最後):str.lastIndexof(char)
回傳 boolean
是否包含指定字串:str.contains(String)
是否以指定字串開頭:str.startsWith(String)
是否以指定字串結尾:str.endsWith(String)
是否為空:str.isEmpty()
回傳 String
擷取字串:str.subString([startIndex])
subString([startIndex],[endIndex]) # 擷取不含 endIndex
轉換大寫:str.toUpperCase()
轉換小寫:str.toLowerCase()
去除首尾空白:str.trim()
合併文字:"str1".concat("str2")
String.join("To ", "concatenate ", "the ", "Strings.")
取代文字:"str1".replace('s', '1')