兩個常用但容易被小看的型別,放一起講。enum 不是 int 的別名,它本身就是 class——能帶欄位、帶方法,甚至能當策略物件用。String 則是 immutable,所有 replace / toUpperCase 都會產生新物件;先理解這點,才知道 StringBuilder 是來幹嘛的。
enum 基本
enum 定義一組固定常數,本質是 class。
enum Status {
PENDING, RUNNING, DONE, FAILED
}
Status s = Status.RUNNING;
if (s == Status.DONE) { ... } // 用 == 比較沒問題(單例)
enum 在 switch
switch (status) {
case PENDING -> "等候中";
case RUNNING -> "執行中";
case DONE -> "完成";
case FAILED -> "失敗";
}
case 裡直接寫常數名就好,不用加 Status. 前綴。
Java 21+ 的 switch 還支援 pattern matching,窮舉檢查也更明確——少寫一個 case 編譯器會直接告訴你。
enum 帶屬性與方法
enum 可以有欄位、建構子、方法,每個常數還能帶不同參數:
enum HttpStatus {
OK(200, "OK"),
NOT_FOUND(404, "Not Found"),
SERVER_ERROR(500, "Internal Server Error");
private final int code;
private final String message;
HttpStatus(int code, String message) { // 建構子隱含 private
this.code = code;
this.message = message;
}
public int code() { return code; }
public String message() { return message; }
}
HttpStatus.NOT_FOUND.code(); // 404
更進一步,每個常數可以各自覆寫方法:
enum Op {
ADD { public int apply(int a, int b) { return a + b; } },
MUL { public int apply(int a, int b) { return a * b; } };
public abstract int apply(int a, int b);
}
Op.ADD.apply(2, 3); // 5
背後的機制:每個帶大括號的常數其實是 enum 的一個匿名 subclass,{ ... } 裡的方法覆寫了 enum 層級宣告的抽象(或具象)method。所以 Op.ADD.apply(1, 2) 走的是 ADD 這個 anonymous subclass 自己的實作。
enum 內建方法
Status.values() // 所有常數陣列
Status.valueOf("DONE") // 字串轉 enum,找不到丟 IllegalArgumentException
s.name() // 常數名 "DONE"
s.ordinal() // 在宣告中的索引(少用,重排會壞)
EnumSet / EnumMap
針對 enum 特化的集合,比 HashSet / HashMap 快很多:
EnumSet<Status> active = EnumSet.of(Status.PENDING, Status.RUNNING);
EnumSet<Status> all = EnumSet.allOf(Status.class);
EnumMap<Status, String> labels = new EnumMap<>(Status.class);
labels.put(Status.DONE, "完成");
只要 key 是 enum,一律用這兩個。
String 是不可變的
String s = "hello";
s.toUpperCase(); // 回傳新字串,s 本身沒變
s = s.toUpperCase(); // 想改要重新賦值
每個 String 操作都產生新物件。所以重複拼接會很傷,要改用 StringBuilder(後面會講)。
String pool
字串字面量會被放進 string pool 重用:
String a = "hello";
String b = "hello";
a == b; // true(同一個池中物件)
String c = new String("hello");
a == c; // false(new 強制建新物件)
a.equals(c); // true(內容比較)
字串比較永遠用 .equals(),不要用 ==。
s.intern() 會把字串放回池中並回傳池中那個。少用,現在 JVM 自動處理得很好。
常用 String 方法
"hello".length(); // 5
"hello".charAt(0); // 'h'
"hello".substring(1, 4); // "ell"(含頭不含尾)
"hello".indexOf("l"); // 2
"hello".replace('l', 'L'); // "heLLo"
"hello".contains("ell"); // true
"hello".startsWith("he"); // true
"hello".endsWith("lo"); // true
" x ".trim(); // "x"(去頭尾空白)
" x ".strip(); // "x"(Java 11+,支援 Unicode 空白)
"a,b,c".split(","); // ["a","b","c"]
String.join("-", "a","b","c");// "a-b-c"
"abc".isEmpty(); // false
" ".isBlank(); // true(Java 11+)
"abc".repeat(3); // "abcabcabc"(Java 11+)
"ABC".toLowerCase();
"abc".toUpperCase();
String.valueOf(123); // "123"
Integer.parseInt("123"); // 123
StringBuilder:可變字串
頻繁拼接、迴圈裡組字串,一律用 StringBuilder,省掉每次都生一個新 String 的成本:
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append(i).append(",");
}
String result = sb.toString();
常用 API:
sb.append("x");
sb.insert(0, "head");
sb.delete(0, 4);
sb.reverse();
sb.setLength(0); // 清空
sb.length();
StringBuffer 是 StringBuilder 的執行緒安全版,但幾乎用不到——單執行緒場景 StringBuilder 就夠;多執行緒下你大概也不會這樣拼字串。
字串格式化
String s = String.format("name=%s, age=%d", "Jeremy", 30);
System.out.printf("name=%s, age=%d%n", "Jeremy", 30);
常用佔位符:
| 意義 | |
|---|---|
%s | 字串 |
%d | 整數 |
%f / %.2f | 浮點 / 兩位小數 |
%n | 換行(跨平台) |
%5d | 寬度 5 右對齊 |
%-5d | 寬度 5 左對齊 |
%05d | 寬度 5 補 0 |
%x / %o | 16 / 8 進位 |
Java 15+ 推薦用 String.formatted,讀起來更順:
"name=%s, age=%d".formatted("Jeremy", 30);
text block(Java 15+)
三引號多行字串,自動處理共同縮排:
String json = """
{
"name": "Jeremy",
"age": 30
}
""";
寫 SQL、JSON、HTML 都好用。要內嵌變數就配 formatted:
String sql = """
SELECT *
FROM users
WHERE id = %d
""".formatted(userId);
char 與 Unicode 注意
char 是 16-bit,存不下所有 Unicode 字元——emoji 和部分漢字是 surrogate pair,要占 2 個 char。
"😀".length(); // 2,不是 1
"😀".codePointCount(0, "😀".length()); // 1
要正確處理就用 codePoints() stream:
"abc😀".codePoints().forEach(System.out::println);
一般場景不太會遇到,做文字處理(截斷、計數)才要小心。
下一篇進檔案 IO——舊的 java.io 與新的 java.nio.file(Path / Files),以及為什麼新程式碼一律用後者。
Latest Updates
- 2026.06.11 Content updated
