本教材涵蓋 Java SE 的完整語法,從基礎到進階,適合初學者與有經驗的開發者參考。 範例基於 Java 17+(LTS),並涵蓋至 Java 21 的新特性。


目錄

  1. Java 簡介與環境設定
  2. 基本程式結構
  3. 資料型別
  4. 變數與常數
  5. 運算子
  6. 流程控制
  7. 陣列
  8. 方法(Methods)
  9. 類別與物件
  10. 建構子
  11. 封裝(Encapsulation)
  12. 繼承(Inheritance)
  13. 多型(Polymorphism)
  14. 抽象類別與介面
  15. 內部類別
  16. 列舉(Enum)
  17. 例外處理
  18. 字串處理
  19. 泛型(Generics)
  20. 集合框架(Collections)
  21. Lambda 表達式與函數式介面
  22. Stream API
  23. I/O 與 NIO
  24. 多執行緒與並行處理
  25. 註解(Annotations)
  26. 套件與模組系統
  27. Record 類別(Java 14+)
  28. 密封類別(Sealed Classes, Java 17+)
  29. 模式比對(Pattern Matching)
  30. 其他實用語法

1. Java 簡介與環境設定

1.1 Java 的特性

  • 跨平台:Write Once, Run Anywhere(WORA),透過 JVM 執行
  • 物件導向:一切皆物件(基本型別除外)
  • 強型別:編譯時期型別檢查
  • 自動記憶體管理:垃圾回收機制(Garbage Collection)
  • 豐富的標準函式庫:java.lang、java.util、java.io 等
  • 多執行緒支援:內建並行處理能力

1.2 JDK、JRE、JVM 的關係

JDK(Java Development Kit)
├── JRE(Java Runtime Environment)
│   ├── JVM(Java Virtual Machine)
│   └── 核心類別函式庫
├── 編譯器(javac)
├── 除錯器(jdb)
└── 其他開發工具

1.3 第一支 Java 程式

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

編譯與執行:

javac HelloWorld.java   # 編譯產生 HelloWorld.class
java HelloWorld          # 執行

1.4 程式進入點

每個 Java 應用程式必須有一個 main 方法作為進入點:

public static void main(String[] args) { }
  • public:JVM 需要從外部呼叫
  • static:不需建立物件即可呼叫
  • void:不回傳值
  • String[] args:命令列參數

2. 基本程式結構

2.1 註解(Comments)

// 單行註解

/* 多行註解
   可以跨越多行 */

/**
 * Javadoc 文件註解
 * @author Nelson
 * @version 1.0
 * @param args 命令列參數
 * @return 無回傳值
 */

2.2 命名慣例

類型 慣例 範例
類別 PascalCase StudentRecord
方法 camelCase calculateTotal()
變數 camelCase firstName
常數 UPPER_SNAKE_CASE MAX_SIZE
套件 全小寫 com.example.app
介面 PascalCase Comparable

2.3 關鍵字(Reserved Words)

Java 共有 50+ 個保留字,不可作為識別字使用:

abstract   assert     boolean    break      byte
case       catch      char       class      const
continue   default    do         double     else
enum       extends    final      finally    float
for        goto       if         implements import
instanceof int        interface  long       native
new        package    private    protected  public
return     short      static     strictfp   super
switch     synchronized this     throw      throws
transient  try        void       volatile   while

另有保留字面值:truefalsenull

2.4 分號與區塊

int x = 10;              // 每個陳述句以分號結尾

{                         // 程式區塊以大括號界定
    int y = 20;
    System.out.println(y);
}

3. 資料型別

3.1 基本型別(Primitive Types)

Java 有 8 種基本型別:

型別 大小 範圍 預設值 包裝類別
byte 8 bit -128 ~ 127 0 Byte
short 16 bit -32,768 ~ 32,767 0 Short
int 32 bit -2³¹ ~ 2³¹-1 0 Integer
long 64 bit -2⁶³ ~ 2⁶³-1 0L Long
float 32 bit IEEE 754 單精度 0.0f Float
double 64 bit IEEE 754 雙精度 0.0d Double
char 16 bit '\u0000' ~ '\uffff' '\u0000' Character
boolean 未定義 true / false false Boolean
byte b = 127;
short s = 32767;
int i = 2_147_483_647;       // 可用底線分隔提升可讀性
long l = 9_223_372_036_854_775_807L;  // long 字面值需加 L
float f = 3.14f;              // float 字面值需加 f
double d = 3.141592653589793;
char c = 'A';                 // 字元用單引號
boolean flag = true;

3.2 字面值(Literals)

// 整數字面值
int decimal = 42;             // 十進位
int hex = 0x2A;               // 十六進位(0x 開頭)
int octal = 052;              // 八進位(0 開頭)
int binary = 0b0010_1010;     // 二進位(0b 開頭,Java 7+)

// 浮點數字面值
double d1 = 3.14;
double d2 = 3.14e2;           // 科學記號 = 314.0
float f1 = 3.14f;

// 字元字面值
char c1 = 'A';
char c2 = '\n';               // 跳脫字元
char c3 = '\u0041';           // Unicode(= 'A')

// 字串字面值
String str = "Hello, Java!";

// 文字區塊(Text Blocks, Java 13+)
String textBlock = """
        SELECT id, name
        FROM users
        WHERE active = true
        """;

3.3 跳脫字元

跳脫序列 說明
\n 換行
\t Tab
\r 歸位
\\ 反斜線
\' 單引號
\" 雙引號
\uXXXX Unicode 字元

3.4 型別轉換

// 自動轉換(隱式轉換)— 小範圍轉大範圍
int i = 100;
long l = i;           // int → long
double d = l;         // long → double

// 強制轉換(顯式轉換)— 大範圍轉小範圍,可能遺失精度
double d2 = 3.99;
int i2 = (int) d2;   // 結果為 3(截斷小數)

long l2 = 130;
byte b = (byte) l2;  // 結果為 -126(溢位)

// 自動提升規則
byte a = 10;
byte b2 = 20;
// byte c = a + b2;  // 編譯錯誤!byte 運算會自動提升為 int
int c = a + b2;      // 正確

轉換路徑:

byte → short → int → long → float → double
              ↑
             char

3.5 自動裝箱與拆箱(Autoboxing / Unboxing)

// 自動裝箱:基本型別 → 包裝類別
Integer obj = 42;               // 等同於 Integer.valueOf(42)

// 自動拆箱:包裝類別 → 基本型別
int value = obj;                // 等同於 obj.intValue()

// 注意:null 拆箱會拋出 NullPointerException
Integer nullObj = null;
// int x = nullObj;             // 執行時期 NullPointerException

// 快取陷阱:Integer 快取 -128 ~ 127
Integer a = 127;
Integer b = 127;
System.out.println(a == b);     // true(來自快取)

Integer c = 128;
Integer d = 128;
System.out.println(c == d);     // false(不同物件)
System.out.println(c.equals(d)); // true(比較值)

4. 變數與常數

4.1 變數宣告

// 宣告並初始化
int age = 25;
String name = "Java";

// 先宣告,再指派
double salary;
salary = 50000.0;

// 同時宣告多個變數
int x = 1, y = 2, z = 3;

4.2 變數種類

public class VariableTypes {
    // 實例變數(Instance Variable)— 屬於物件
    private String instanceVar = "instance";

    // 類別變數(Class/Static Variable)— 屬於類別
    private static int classVar = 0;

    public void method() {
        // 區域變數(Local Variable)— 屬於方法
        int localVar = 10;

        // 區域變數必須初始化後才能使用
        int uninitVar;
        // System.out.println(uninitVar); // 編譯錯誤
    }

    // 參數(Parameter)— 方法接收的引數
    public void setName(String name) {
        this.instanceVar = name;
    }
}

4.3 常數(final)

// 常數:一旦指派就不能再修改
final int MAX_SIZE = 100;
final double PI = 3.14159265358979;

// 空白 final:宣告時不初始化,但必須在建構子中指派
class Circle {
    final double radius;

    Circle(double r) {
        this.radius = r;  // 必須在此初始化
    }
}

// static final:類別等級的常數
public static final String APP_NAME = "MyApp";

4.4 var 區域變數型別推斷(Java 10+)

var list = new ArrayList<String>();   // 推斷為 ArrayList<String>
var stream = list.stream();           // 推斷為 Stream<String>
var x = 10;                           // 推斷為 int
var name = "Java";                    // 推斷為 String

// 限制:
// 1. 只能用於區域變數
// 2. 必須有初始值
// 3. 不能用於方法參數、回傳型別、成員變數
// var y;               // 編譯錯誤:無法推斷
// var z = null;         // 編譯錯誤:無法推斷
// var arr = {1, 2, 3};  // 編譯錯誤:無法推斷

// 可用於 for 迴圈
for (var item : list) {
    System.out.println(item);
}

// 可用於 try-with-resources
try (var reader = new BufferedReader(new FileReader("file.txt"))) {
    var line = reader.readLine();
}

5. 運算子

5.1 算術運算子

int a = 10, b = 3;

a + b    // 13  加法
a - b    // 7   減法
a * b    // 30  乘法
a / b    // 3   整數除法(截斷小數)
a % b    // 1   取餘數

10.0 / 3  // 3.333...  浮點除法

// 一元運算子
+a       // 正號
-a       // 負號(-10)

5.2 遞增與遞減運算子

int x = 5;

x++      // 後置遞增:先取值再加 1
++x      // 前置遞增:先加 1 再取值
x--      // 後置遞減
--x      // 前置遞減

// 差異示範
int a = 5;
int b = a++;  // b = 5, a = 6
int c = ++a;  // a = 7, c = 7

5.3 指派運算子

int x = 10;

x += 5;   // x = x + 5  → 15
x -= 3;   // x = x - 3  → 12
x *= 2;   // x = x * 2  → 24
x /= 4;   // x = x / 4  → 6
x %= 4;   // x = x % 4  → 2
x &= 3;   // x = x & 3  位元 AND
x |= 5;   // x = x | 5  位元 OR
x ^= 2;   // x = x ^ 2  位元 XOR
x <<= 1;  // x = x << 1 左移
x >>= 1;  // x = x >> 1 右移(帶正負號)
x >>>= 1; // x = x >>> 1 右移(無正負號)

5.4 比較運算子

int a = 10, b = 20;

a == b    // false  等於
a != b    // true   不等於
a > b     // false  大於
a < b     // true   小於
a >= b    // false  大於等於
a <= b    // true   小於等於

// 物件比較
String s1 = new String("hello");
String s2 = new String("hello");
s1 == s2       // false(比較參考位址)
s1.equals(s2)  // true(比較內容)

5.5 邏輯運算子

boolean a = true, b = false;

a && b    // false  邏輯 AND(短路求值)
a || b    // true   邏輯 OR(短路求值)
!a        // false  邏輯 NOT

a & b     // false  邏輯 AND(不短路)
a | b     // true   邏輯 OR(不短路)
a ^ b     // true   邏輯 XOR

// 短路求值示範
String str = null;
if (str != null && str.length() > 0) {
    // 因為短路,str 為 null 時不會呼叫 length()
}

5.6 位元運算子

int a = 0b1010;  // 10
int b = 0b1100;  // 12

a & b     // 0b1000 = 8   AND
a | b     // 0b1110 = 14  OR
a ^ b     // 0b0110 = 6   XOR
~a        // 反轉所有位元   NOT

a << 2    // 0b101000 = 40   左移
a >> 1    // 0b0101 = 5      右移(算術,保留正負號)
a >>> 1   // 0b0101 = 5      右移(邏輯,補 0)

5.7 三元運算子

int x = 10;
String result = (x > 5) ? "大於 5" : "小於等於 5";

// 可巢狀使用(但建議避免過度巢狀)
String grade = (score >= 90) ? "A"
             : (score >= 80) ? "B"
             : (score >= 70) ? "C"
             : "F";

5.8 instanceof 運算子

Object obj = "Hello";

if (obj instanceof String) {
    String str = (String) obj;
    System.out.println(str.length());
}

// 模式比對的 instanceof(Java 16+)
if (obj instanceof String str) {
    System.out.println(str.length());  // 直接使用 str,不需強制轉型
}

// 也可在條件中使用
if (obj instanceof String str && str.length() > 3) {
    System.out.println(str);
}

5.9 運算子優先順序(由高至低)

優先順序 運算子 結合性
1 () [] . 左 → 右
2 ++ -- + - ~ ! (一元) (type) 右 → 左
3 * / % 左 → 右
4 + - 左 → 右
5 << >> >>> 左 → 右
6 < <= > >= instanceof 左 → 右
7 == != 左 → 右
8 & 左 → 右
9 ^ 左 → 右
10 \| 左 → 右
11 && 左 → 右
12 \|\| 左 → 右
13 ? : 右 → 左
14 = += -= 右 → 左

6. 流程控制

6.1 if-else

int score = 85;

if (score >= 90) {
    System.out.println("A");
} else if (score >= 80) {
    System.out.println("B");
} else if (score >= 70) {
    System.out.println("C");
} else {
    System.out.println("F");
}

6.2 switch 陳述句

// 傳統 switch
int day = 3;
switch (day) {
    case 1:
        System.out.println("Monday");
        break;
    case 2:
        System.out.println("Tuesday");
        break;
    case 3:
        System.out.println("Wednesday");
        break;
    default:
        System.out.println("Other");
        break;
}

// switch 可用的型別:byte, short, int, char, String, enum

// 多值 case(Java 14+)
switch (day) {
    case 1, 7:
        System.out.println("Weekend");
        break;
    case 2, 3, 4, 5, 6:
        System.out.println("Weekday");
        break;
}

6.3 switch 表達式(Java 14+)

// 箭頭語法(不需 break)
String dayName = switch (day) {
    case 1 -> "Monday";
    case 2 -> "Tuesday";
    case 3 -> "Wednesday";
    case 4 -> "Thursday";
    case 5 -> "Friday";
    case 6 -> "Saturday";
    case 7 -> "Sunday";
    default -> "Invalid";
};

// 使用 yield 回傳值(需要多行邏輯時)
String result = switch (day) {
    case 1 -> "Monday";
    case 2 -> {
        String name = "Tues" + "day";
        yield name;
    }
    default -> "Other";
};

// 模式比對 switch(Java 21+)
Object obj = "Hello";
String formatted = switch (obj) {
    case Integer i -> "Integer: " + i;
    case Long l    -> "Long: " + l;
    case Double d  -> "Double: " + d;
    case String s  -> "String: " + s;
    case null      -> "null";
    default        -> "Unknown: " + obj;
};

// 守衛條件(Guarded Patterns, Java 21+)
String category = switch (obj) {
    case Integer i when i > 0  -> "正整數";
    case Integer i when i == 0 -> "零";
    case Integer i             -> "負整數";
    case String s when s.isEmpty() -> "空字串";
    case String s              -> "字串: " + s;
    default                    -> "其他";
};

6.4 for 迴圈

// 基本 for 迴圈
for (int i = 0; i < 10; i++) {
    System.out.println(i);
}

// 多重初始化
for (int i = 0, j = 10; i < j; i++, j--) {
    System.out.println(i + " " + j);
}

// 無限迴圈
for (;;) {
    // ...
    break;
}

6.5 增強 for 迴圈(for-each)

int[] numbers = {1, 2, 3, 4, 5};
for (int num : numbers) {
    System.out.println(num);
}

List<String> names = List.of("Alice", "Bob", "Charlie");
for (String name : names) {
    System.out.println(name);
}

// 只要實作 Iterable<T> 介面的物件都可使用 for-each

6.6 while 迴圈

int count = 0;
while (count < 5) {
    System.out.println(count);
    count++;
}

6.7 do-while 迴圈

int count = 0;
do {
    System.out.println(count);
    count++;
} while (count < 5);  // 至少執行一次

6.8 break 與 continue

// break:跳出迴圈
for (int i = 0; i < 10; i++) {
    if (i == 5) break;
    System.out.println(i);  // 印出 0 ~ 4
}

// continue:跳過本次迭代
for (int i = 0; i < 10; i++) {
    if (i % 2 == 0) continue;
    System.out.println(i);  // 印出 1, 3, 5, 7, 9
}

// 標籤(Label)— 用於巢狀迴圈
outer:
for (int i = 0; i < 5; i++) {
    for (int j = 0; j < 5; j++) {
        if (i * j > 6) break outer;  // 跳出外層迴圈
        System.out.println(i + " * " + j + " = " + (i * j));
    }
}

7. 陣列

7.1 一維陣列

// 宣告與建立
int[] numbers = new int[5];           // 宣告長度為 5 的整數陣列
String[] names = new String[3];       // 宣告長度為 3 的字串陣列

// 宣告並初始化
int[] scores = {90, 85, 78, 92, 88};
int[] scores2 = new int[]{90, 85, 78};

// 存取元素
scores[0] = 95;                       // 設定第一個元素
int first = scores[0];                // 取得第一個元素

// 陣列長度
int len = scores.length;              // 注意:length 是屬性,不是方法

// 走訪
for (int i = 0; i < scores.length; i++) {
    System.out.println(scores[i]);
}

for (int score : scores) {
    System.out.println(score);
}

7.2 多維陣列

// 二維陣列
int[][] matrix = new int[3][4];       // 3 列 4 行
int[][] grid = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};

// 不規則陣列(Jagged Array)
int[][] jagged = new int[3][];
jagged[0] = new int[]{1, 2};
jagged[1] = new int[]{3, 4, 5};
jagged[2] = new int[]{6};

// 走訪二維陣列
for (int i = 0; i < grid.length; i++) {
    for (int j = 0; j < grid[i].length; j++) {
        System.out.print(grid[i][j] + " ");
    }
    System.out.println();
}

// 三維陣列
int[][][] cube = new int[2][3][4];

7.3 Arrays 工具類別

import java.util.Arrays;

int[] arr = {5, 3, 1, 4, 2};

Arrays.sort(arr);                     // 排序:{1, 2, 3, 4, 5}
Arrays.fill(arr, 0);                  // 全部填入 0
int idx = Arrays.binarySearch(arr, 3); // 二元搜尋(需先排序)
int[] copy = Arrays.copyOf(arr, 10);  // 複製(新長度 10)
int[] range = Arrays.copyOfRange(arr, 1, 3); // 複製片段 [1, 3)

boolean eq = Arrays.equals(arr, copy); // 比較內容是否相同
String str = Arrays.toString(arr);    // 轉為字串 "[1, 2, 3, 4, 5]"
String deep = Arrays.deepToString(grid); // 深層轉字串(多維)

// 平行排序(Java 8+)
Arrays.parallelSort(arr);

8. 方法(Methods)

8.1 方法定義

// 語法:[修飾子] 回傳型別 方法名稱(參數列) [throws 例外] { 方法主體 }

public int add(int a, int b) {
    return a + b;
}

public void greet(String name) {
    System.out.println("Hello, " + name);
}

private static double calculateArea(double radius) {
    return Math.PI * radius * radius;
}

8.2 方法多載(Overloading)

public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }

    public double add(double a, double b) {
        return a + b;
    }

    public int add(int a, int b, int c) {
        return a + b + c;
    }

    // 方法多載取決於:參數數量、參數型別、參數順序
    // 不取決於:回傳型別、修飾子
}

8.3 可變長度參數(Varargs)

public int sum(int... numbers) {
    int total = 0;
    for (int n : numbers) {
        total += n;
    }
    return total;
}

// 呼叫
sum(1, 2, 3);           // 6
sum(1, 2, 3, 4, 5);     // 15
sum();                    // 0

// varargs 必須是最後一個參數
public void log(String tag, String... messages) { }

8.4 遞迴(Recursion)

public long factorial(int n) {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}

public int fibonacci(int n) {
    if (n <= 1) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

8.5 值傳遞(Pass by Value)

Java 永遠是「值傳遞」:

// 基本型別:傳遞的是值的複本
public void modify(int x) {
    x = 100;  // 不影響呼叫端的變數
}

// 參考型別:傳遞的是參考的複本(指向同一物件)
public void modify(int[] arr) {
    arr[0] = 100;  // 會影響呼叫端的陣列
}

public void reassign(int[] arr) {
    arr = new int[]{100, 200};  // 不影響呼叫端的參考
}

9. 類別與物件

9.1 類別定義

public class Person {
    // 成員變數(Fields)
    private String name;
    private int age;

    // 方法(Methods)
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;  // this 指向目前物件
    }

    public void introduce() {
        System.out.println("我是 " + name + ",今年 " + age + " 歲");
    }
}

9.2 建立物件

Person person = new Person();
person.setName("Alice");
person.introduce();

// 物件的生命週期
// 1. 宣告參考變數
// 2. new 配置記憶體
// 3. 呼叫建構子初始化
// 4. 使用物件
// 5. 無參考指向時,由 GC 回收

9.3 存取修飾子

修飾子 同類別 同套件 子類別 全域
public
protected
預設(無修飾子)
private

9.4 static 關鍵字

public class Counter {
    private static int count = 0;      // 類別變數:所有物件共享

    public Counter() {
        count++;
    }

    public static int getCount() {     // 類別方法
        return count;
    }

    // 靜態初始化區塊
    static {
        System.out.println("類別載入時執行");
    }

    // 靜態內部類別
    static class Helper {
        // ...
    }
}

// 使用
Counter.getCount();                    // 透過類別名稱呼叫

9.5 this 關鍵字

public class Student {
    private String name;
    private int grade;

    public Student(String name, int grade) {
        this.name = name;     // 區分成員變數與參數
        this.grade = grade;
    }

    public Student(String name) {
        this(name, 1);        // 呼叫另一個建構子(必須在第一行)
    }

    public Student self() {
        return this;          // 回傳自身(用於方法鏈)
    }
}

10. 建構子

10.1 建構子基礎

public class Book {
    private String title;
    private String author;
    private int pages;

    // 無參數建構子
    public Book() {
        this.title = "Unknown";
        this.author = "Unknown";
        this.pages = 0;
    }

    // 有參數建構子
    public Book(String title, String author, int pages) {
        this.title = title;
        this.author = author;
        this.pages = pages;
    }

    // 建構子多載
    public Book(String title, String author) {
        this(title, author, 0);  // 呼叫另一個建構子
    }
}

10.2 預設建構子

// 如果沒有定義任何建構子,編譯器會自動產生預設建構子:
// public Book() { }

// 但一旦定義了任何建構子,預設建構子就不會自動產生

10.3 複製建構子

public class Point {
    private int x, y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    // 複製建構子
    public Point(Point other) {
        this.x = other.x;
        this.y = other.y;
    }
}

Point p1 = new Point(10, 20);
Point p2 = new Point(p1);  // 複製 p1

10.4 初始化區塊

public class InitDemo {
    private int value;

    // 靜態初始化區塊(類別載入時執行一次)
    static {
        System.out.println("Static initializer");
    }

    // 實例初始化區塊(每次建立物件時執行,在建構子之前)
    {
        value = 42;
        System.out.println("Instance initializer");
    }

    public InitDemo() {
        System.out.println("Constructor");
    }
}

// 執行順序:Static initializer → Instance initializer → Constructor

11. 封裝(Encapsulation)

11.1 Getter 與 Setter

public class BankAccount {
    private double balance;
    private String owner;

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        if (balance < 0) {
            throw new IllegalArgumentException("餘額不能為負數");
        }
        this.balance = balance;
    }

    public String getOwner() {
        return owner;
    }

    public void setOwner(String owner) {
        if (owner == null || owner.isBlank()) {
            throw new IllegalArgumentException("姓名不能為空");
        }
        this.owner = owner;
    }
}

11.2 不可變物件(Immutable Object)

public final class ImmutablePerson {
    private final String name;
    private final int age;
    private final List<String> hobbies;

    public ImmutablePerson(String name, int age, List<String> hobbies) {
        this.name = name;
        this.age = age;
        this.hobbies = List.copyOf(hobbies);  // 防禦性複製
    }

    public String getName() { return name; }
    public int getAge() { return age; }
    public List<String> getHobbies() { return hobbies; }  // 已是不可變 List
}

12. 繼承(Inheritance)

12.1 基本繼承

// 父類別(超類別)
public class Animal {
    protected String name;
    protected int age;

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void eat() {
        System.out.println(name + " is eating");
    }

    public void sleep() {
        System.out.println(name + " is sleeping");
    }
}

// 子類別
public class Dog extends Animal {
    private String breed;

    public Dog(String name, int age, String breed) {
        super(name, age);      // 呼叫父類別建構子(必須在第一行)
        this.breed = breed;
    }

    public void bark() {
        System.out.println(name + " says: Woof!");
    }

    @Override
    public void eat() {
        super.eat();           // 呼叫父類別方法
        System.out.println(name + " is eating dog food");
    }
}

12.2 方法覆寫(Override)

public class Shape {
    public double area() {
        return 0;
    }
}

public class Circle extends Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    @Override                            // 建議加上此註解
    public double area() {
        return Math.PI * radius * radius;
    }
}

// 覆寫規則:
// 1. 方法名稱和參數必須完全相同
// 2. 回傳型別可以是子型別(協變回傳型別)
// 3. 存取權限不能比父類別更嚴格
// 4. 不能拋出比父類別更多的受檢例外
// 5. final 方法不能被覆寫
// 6. static 方法不能被覆寫(但可以隱藏)
// 7. private 方法不能被覆寫(子類別看不見)

12.3 final 在繼承中的用途

// final 類別:不能被繼承
public final class String { }

// final 方法:不能被覆寫
public class Parent {
    public final void importantMethod() {
        // 子類別無法覆寫此方法
    }
}

// final 變數:不能被重新指派
public final int MAX = 100;

12.4 Object 類別

所有類別都隱式繼承自 java.lang.Object

public class MyClass {
    // 從 Object 繼承的重要方法

    @Override
    public String toString() {
        return "MyClass instance";
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        MyClass other = (MyClass) obj;
        return Objects.equals(this.field, other.field);
    }

    @Override
    public int hashCode() {
        return Objects.hash(field);
    }

    // 其他 Object 方法:
    // getClass()  — 取得執行時期類別
    // clone()     — 複製物件(需實作 Cloneable)
    // finalize()  — GC 前呼叫(已棄用)
    // wait(), notify(), notifyAll() — 執行緒同步
}

13. 多型(Polymorphism)

13.1 編譯時期多型(方法多載)

public class Printer {
    public void print(int x)    { System.out.println("int: " + x); }
    public void print(double x) { System.out.println("double: " + x); }
    public void print(String x) { System.out.println("String: " + x); }
}

13.2 執行時期多型(方法覆寫)

Animal animal = new Dog("Buddy", 3, "Labrador");
animal.eat();    // 呼叫 Dog 的 eat()(動態分派)
// animal.bark(); // 編譯錯誤!Animal 沒有 bark()

// 向上轉型(Upcasting)— 自動
Animal a = new Dog("Rex", 2, "Poodle");

// 向下轉型(Downcasting)— 需強制轉型
if (a instanceof Dog dog) {
    dog.bark();
}

13.3 多型實例

public abstract class Shape {
    public abstract double area();
}

public class Circle extends Shape {
    private double radius;
    public Circle(double r) { this.radius = r; }
    @Override public double area() { return Math.PI * radius * radius; }
}

public class Rectangle extends Shape {
    private double width, height;
    public Rectangle(double w, double h) { this.width = w; this.height = h; }
    @Override public double area() { return width * height; }
}

// 多型的威力
Shape[] shapes = { new Circle(5), new Rectangle(4, 6) };
for (Shape s : shapes) {
    System.out.println("面積: " + s.area());  // 各自呼叫自己的實作
}

14. 抽象類別與介面

14.1 抽象類別

public abstract class Vehicle {
    protected String brand;
    protected int year;

    public Vehicle(String brand, int year) {
        this.brand = brand;
        this.year = year;
    }

    // 抽象方法:沒有實作,子類別必須覆寫
    public abstract void start();
    public abstract double fuelEfficiency();

    // 具體方法:有實作
    public String getInfo() {
        return brand + " (" + year + ")";
    }
}

public class ElectricCar extends Vehicle {
    private double batteryCapacity;

    public ElectricCar(String brand, int year, double battery) {
        super(brand, year);
        this.batteryCapacity = battery;
    }

    @Override
    public void start() {
        System.out.println("Electric motor starting silently...");
    }

    @Override
    public double fuelEfficiency() {
        return batteryCapacity * 5.0;
    }
}

14.2 介面(Interface)

public interface Drawable {
    // 常數(預設為 public static final)
    int MAX_COLORS = 256;

    // 抽象方法(預設為 public abstract)
    void draw();
    void resize(double factor);

    // 預設方法(Java 8+)
    default void display() {
        System.out.println("Displaying...");
        draw();
    }

    // 靜態方法(Java 8+)
    static Drawable empty() {
        return new Drawable() {
            @Override public void draw() { }
            @Override public void resize(double factor) { }
        };
    }

    // 私有方法(Java 9+)
    private void logAction(String action) {
        System.out.println("Action: " + action);
    }
}

// 實作介面
public class Circle implements Drawable {
    @Override
    public void draw() {
        System.out.println("Drawing circle");
    }

    @Override
    public void resize(double factor) {
        System.out.println("Resizing circle by " + factor);
    }
}

14.3 多重繼承(介面)

public interface Flyable {
    void fly();
}

public interface Swimmable {
    void swim();
}

// Java 不支援多重繼承類別,但支援多重實作介面
public class Duck extends Animal implements Flyable, Swimmable {
    @Override public void fly()  { System.out.println("Duck flying"); }
    @Override public void swim() { System.out.println("Duck swimming"); }
}

// 菱形問題的解決
public interface A {
    default void hello() { System.out.println("A"); }
}

public interface B extends A {
    @Override default void hello() { System.out.println("B"); }
}

public class C implements A, B {
    // B 的 hello() 勝出(更具體的介面)
    // 如果兩個介面無繼承關係且都有相同的 default 方法,必須手動覆寫
}

14.4 抽象類別 vs 介面

特性 抽象類別 介面
建構子 ✅ 可以有 ❌ 沒有
成員變數 ✅ 任意 只有 public static final
方法 抽象 + 具體 抽象 + default + static + private
繼承 單一繼承 多重實作
存取修飾子 任意 方法預設 public
適用情境 IS-A 關係、共享狀態 定義能力/契約

15. 內部類別

15.1 成員內部類別

public class Outer {
    private int x = 10;

    public class Inner {
        public void show() {
            System.out.println("x = " + x);  // 可存取外部類別的私有成員
            System.out.println("Outer.this.x = " + Outer.this.x);
        }
    }
}

// 使用
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.show();

15.2 靜態巢狀類別

public class Outer {
    private static int y = 20;

    public static class StaticNested {
        public void show() {
            System.out.println("y = " + y);  // 只能存取靜態成員
        }
    }
}

// 使用
Outer.StaticNested nested = new Outer.StaticNested();
nested.show();

15.3 區域內部類別

public class Outer {
    public void method() {
        final int localVar = 100;

        class LocalInner {
            public void show() {
                System.out.println("localVar = " + localVar);
            }
        }

        LocalInner inner = new LocalInner();
        inner.show();
    }
}

15.4 匿名內部類別

// 繼承類別的匿名內部類別
Runnable task = new Runnable() {
    @Override
    public void run() {
        System.out.println("Running...");
    }
};

// 實作介面的匿名內部類別
Comparator<String> comp = new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.length() - b.length();
    }
};

// 匿名內部類別通常可以用 Lambda 取代(若為函數式介面)
Runnable task2 = () -> System.out.println("Running...");
Comparator<String> comp2 = (a, b) -> a.length() - b.length();

16. 列舉(Enum)

16.1 基本列舉

public enum Season {
    SPRING, SUMMER, AUTUMN, WINTER
}

// 使用
Season s = Season.SPRING;
System.out.println(s);            // SPRING
System.out.println(s.name());     // SPRING
System.out.println(s.ordinal());  // 0

// 走訪
for (Season season : Season.values()) {
    System.out.println(season);
}

// 從字串轉換
Season winter = Season.valueOf("WINTER");

// 在 switch 中使用
switch (s) {
    case SPRING -> System.out.println("春天");
    case SUMMER -> System.out.println("夏天");
    case AUTUMN -> System.out.println("秋天");
    case WINTER -> System.out.println("冬天");
}

16.2 帶屬性和方法的列舉

public enum Planet {
    MERCURY(3.303e+23, 2.4397e6),
    VENUS(4.869e+24, 6.0518e6),
    EARTH(5.976e+24, 6.37814e6),
    MARS(6.421e+23, 3.3972e6);

    private final double mass;
    private final double radius;

    // 建構子必須是 private
    Planet(double mass, double radius) {
        this.mass = mass;
        this.radius = radius;
    }

    public double getMass() { return mass; }
    public double getRadius() { return radius; }

    // 常數 G
    private static final double G = 6.67300E-11;

    public double surfaceGravity() {
        return G * mass / (radius * radius);
    }

    public double surfaceWeight(double otherMass) {
        return otherMass * surfaceGravity();
    }
}

16.3 實作介面的列舉

public enum Operation implements Calculable {
    PLUS("+")   { public double apply(double x, double y) { return x + y; } },
    MINUS("-")  { public double apply(double x, double y) { return x - y; } },
    TIMES("*")  { public double apply(double x, double y) { return x * y; } },
    DIVIDE("/") { public double apply(double x, double y) { return x / y; } };

    private final String symbol;

    Operation(String symbol) { this.symbol = symbol; }

    public abstract double apply(double x, double y);

    @Override
    public String toString() { return symbol; }
}

interface Calculable {
    double apply(double x, double y);
}

17. 例外處理

17.1 例外階層

Throwable
├── Error                        (系統錯誤,通常無法處理)
│   ├── OutOfMemoryError
│   ├── StackOverflowError
│   └── ...
└── Exception
    ├── RuntimeException         (未受檢例外 Unchecked)
    │   ├── NullPointerException
    │   ├── ArrayIndexOutOfBoundsException
    │   ├── ClassCastException
    │   ├── IllegalArgumentException
    │   ├── ArithmeticException
    │   └── ...
    ├── IOException              (受檢例外 Checked)
    ├── SQLException
    └── ...

17.2 try-catch-finally

try {
    int result = 10 / 0;
} catch (ArithmeticException e) {
    System.out.println("算術錯誤: " + e.getMessage());
} catch (Exception e) {
    System.out.println("一般錯誤: " + e.getMessage());
} finally {
    System.out.println("一定會執行");
}

// 多重 catch(Java 7+)
try {
    // ...
} catch (IOException | SQLException e) {
    System.out.println("I/O 或 SQL 錯誤: " + e.getMessage());
}

17.3 try-with-resources(Java 7+)

// 自動關閉實作 AutoCloseable 的資源
try (FileReader fr = new FileReader("file.txt");
     BufferedReader br = new BufferedReader(fr)) {
    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }
}
// 不需手動呼叫 close(),離開 try 區塊時自動關閉

// Java 9+:可以使用已宣告的 effectively final 變數
FileReader fr = new FileReader("file.txt");
BufferedReader br = new BufferedReader(fr);
try (fr; br) {
    // ...
}

17.4 throw 與 throws

// throw:拋出例外
public void withdraw(double amount) {
    if (amount <= 0) {
        throw new IllegalArgumentException("金額必須大於 0");
    }
    if (amount > balance) {
        throw new InsufficientFundsException("餘額不足");
    }
    balance -= amount;
}

// throws:宣告方法可能拋出的受檢例外
public void readFile(String path) throws IOException {
    BufferedReader reader = new BufferedReader(new FileReader(path));
    // ...
}

17.5 自訂例外

// 受檢例外
public class InsufficientFundsException extends Exception {
    private final double amount;

    public InsufficientFundsException(String message) {
        super(message);
        this.amount = 0;
    }

    public InsufficientFundsException(String message, double amount) {
        super(message);
        this.amount = amount;
    }

    public double getAmount() { return amount; }
}

// 未受檢例外
public class InvalidConfigException extends RuntimeException {
    public InvalidConfigException(String message) {
        super(message);
    }

    public InvalidConfigException(String message, Throwable cause) {
        super(message, cause);
    }
}

17.6 例外鏈

try {
    // ...
} catch (SQLException e) {
    throw new DataAccessException("資料庫操作失敗", e);  // 包裝原始例外
}

18. 字串處理

18.1 String

// String 是不可變的(immutable)
String s1 = "Hello";                    // 字串池
String s2 = new String("Hello");        // 堆積
String s3 = "Hello";
System.out.println(s1 == s3);           // true(同一字串池物件)
System.out.println(s1 == s2);           // false(不同物件)
System.out.println(s1.equals(s2));      // true(內容相同)

// 常用方法
String str = "Hello, World!";

str.length()                    // 13
str.charAt(0)                   // 'H'
str.indexOf("World")            // 7
str.lastIndexOf('o')            // 8
str.substring(7)                // "World!"
str.substring(7, 12)            // "World"
str.contains("World")           // true
str.startsWith("Hello")         // true
str.endsWith("!")               // true
str.isEmpty()                   // false
str.isBlank()                   // false(Java 11+)

str.toUpperCase()               // "HELLO, WORLD!"
str.toLowerCase()               // "hello, world!"
str.trim()                      // 去除首尾空白
str.strip()                     // 去除首尾空白(含 Unicode,Java 11+)
str.stripLeading()              // 去除前導空白(Java 11+)
str.stripTrailing()             // 去除尾隨空白(Java 11+)

str.replace("World", "Java")   // "Hello, Java!"
str.replaceAll("\\w+", "*")    // 正規表達式取代
str.split(", ")                 // ["Hello", "World!"]

str.chars()                     // IntStream
str.codePoints()                // IntStream

str.repeat(3)                   // 重複 3 次(Java 11+)

// 字串比較
str.equals("hello")             // false
str.equalsIgnoreCase("hello, world!")  // true
str.compareTo("Hello")          // 依字典順序比較

// 格式化
String formatted = String.format("Name: %s, Age: %d", "Alice", 25);
String formatted2 = "Name: %s, Age: %d".formatted("Alice", 25); // Java 15+

18.2 StringBuilder 與 StringBuffer

// StringBuilder(非執行緒安全,效能較好)
StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(", ");
sb.append("World!");
sb.insert(5, " Beautiful");
sb.delete(5, 15);
sb.replace(7, 12, "Java");
sb.reverse();
String result = sb.toString();

// 方法鏈
String chain = new StringBuilder()
    .append("Hello")
    .append(" ")
    .append("World")
    .toString();

// StringBuffer(執行緒安全,效能較差)
StringBuffer sbf = new StringBuffer("Hello");
// 方法與 StringBuilder 相同,但所有方法都是 synchronized

18.3 字串連接

// + 運算子(編譯器優化為 StringBuilder)
String s = "Hello" + " " + "World";

// String.join()(Java 8+)
String joined = String.join(", ", "a", "b", "c");  // "a, b, c"
String joined2 = String.join("-", List.of("2024", "01", "15"));

// StringJoiner(Java 8+)
StringJoiner sj = new StringJoiner(", ", "[", "]");
sj.add("a").add("b").add("c");
System.out.println(sj);  // [a, b, c]

// Collectors.joining()
String csv = List.of("a", "b", "c")
    .stream()
    .collect(Collectors.joining(", "));

19. 泛型(Generics)

19.1 泛型類別

public class Box<T> {
    private T content;

    public Box(T content) {
        this.content = content;
    }

    public T getContent() { return content; }
    public void setContent(T content) { this.content = content; }
}

// 使用
Box<String> stringBox = new Box<>("Hello");
Box<Integer> intBox = new Box<>(42);

// 多型別參數
public class Pair<K, V> {
    private K key;
    private V value;

    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public K getKey() { return key; }
    public V getValue() { return value; }
}

Pair<String, Integer> pair = new Pair<>("age", 25);

19.2 泛型方法

public class Util {
    // 泛型方法
    public static <T> T getFirst(List<T> list) {
        if (list.isEmpty()) return null;
        return list.get(0);
    }

    // 多型別參數
    public static <T, U> Pair<T, U> makePair(T first, U second) {
        return new Pair<>(first, second);
    }

    // 有界型別參數
    public static <T extends Comparable<T>> T max(T a, T b) {
        return a.compareTo(b) >= 0 ? a : b;
    }
}

// 呼叫(型別推斷)
String first = Util.getFirst(List.of("a", "b", "c"));
int maxVal = Util.max(3, 7);

// 明確指定型別
String first2 = Util.<String>getFirst(List.of("a", "b"));

19.3 泛型介面

public interface Repository<T, ID> {
    T findById(ID id);
    List<T> findAll();
    void save(T entity);
    void deleteById(ID id);
}

public class UserRepository implements Repository<User, Long> {
    @Override public User findById(Long id) { /* ... */ return null; }
    @Override public List<User> findAll() { /* ... */ return null; }
    @Override public void save(User entity) { /* ... */ }
    @Override public void deleteById(Long id) { /* ... */ }
}

19.4 型別邊界(Bounds)

// 上界(extends)
public <T extends Number> double sum(List<T> list) {
    double total = 0;
    for (T num : list) {
        total += num.doubleValue();
    }
    return total;
}

// 多重邊界
public <T extends Comparable<T> & Serializable> void process(T item) {
    // T 必須同時實作 Comparable 和 Serializable
}

19.5 萬用字元(Wildcards)

// 無界萬用字元
public void printList(List<?> list) {
    for (Object item : list) {
        System.out.println(item);
    }
}

// 上界萬用字元(讀取用)— Producer Extends
public double sum(List<? extends Number> list) {
    double total = 0;
    for (Number num : list) {
        total += num.doubleValue();
    }
    return total;
}
sum(List.of(1, 2, 3));        // List<Integer>
sum(List.of(1.0, 2.0, 3.0));  // List<Double>

// 下界萬用字元(寫入用)— Consumer Super
public void addIntegers(List<? super Integer> list) {
    list.add(1);
    list.add(2);
    list.add(3);
}

// PECS 原則:Producer Extends, Consumer Super
// 讀取用 extends,寫入用 super

19.6 型別擦除(Type Erasure)

// 泛型只在編譯時期存在,執行時期會被擦除
// Box<String> 和 Box<Integer> 在執行時期都是 Box

// 限制
// 1. 無法建立泛型型別的實例
//    T obj = new T();                // 編譯錯誤
// 2. 無法建立泛型陣列
//    T[] arr = new T[10];            // 編譯錯誤
// 3. 無法使用 instanceof 檢查泛型型別
//    if (obj instanceof List<String>) { }  // 編譯錯誤
// 4. 無法建立泛型的靜態成員
//    static T instance;              // 編譯錯誤

20. 集合框架(Collections)

20.1 集合階層

Iterable
└── Collection
    ├── List(有序、可重複)
    │   ├── ArrayList
    │   ├── LinkedList
    │   ├── Vector(已過時)
    │   └── Stack(已過時)
    ├── Set(無序、不重複)
    │   ├── HashSet
    │   ├── LinkedHashSet
    │   └── TreeSet(有序)
    └── Queue(佇列)
        ├── LinkedList
        ├── PriorityQueue
        └── Deque
            ├── ArrayDeque
            └── LinkedList

Map(鍵值對)
├── HashMap
├── LinkedHashMap
├── TreeMap(有序)
├── Hashtable(已過時)
└── ConcurrentHashMap

20.2 List

// ArrayList — 動態陣列,隨機存取快,插入刪除慢
List<String> list = new ArrayList<>();
list.add("Alice");
list.add("Bob");
list.add(1, "Charlie");       // 在索引 1 插入
list.get(0);                  // "Alice"
list.set(0, "Eve");           // 替換索引 0
list.remove(0);               // 移除索引 0
list.remove("Bob");           // 移除第一個 "Bob"
list.size();                  // 元素數量
list.contains("Charlie");     // 是否包含
list.indexOf("Charlie");      // 索引位置
list.isEmpty();               // 是否為空
list.clear();                 // 清空

// LinkedList — 雙向鏈結串列,插入刪除快,隨機存取慢
List<String> linked = new LinkedList<>();
linked.add("A");
((LinkedList<String>) linked).addFirst("B");
((LinkedList<String>) linked).addLast("C");

// 不可變 List(Java 9+)
List<String> immutable = List.of("a", "b", "c");
// immutable.add("d");        // UnsupportedOperationException

// 可變副本
List<String> mutable = new ArrayList<>(List.of("a", "b", "c"));

// 排序
list.sort(Comparator.naturalOrder());
list.sort(Comparator.reverseOrder());
list.sort(Comparator.comparing(String::length));
Collections.sort(list);

// 走訪
for (String item : list) { }
list.forEach(System.out::println);
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    String item = it.next();
    if (item.equals("remove")) it.remove();
}
ListIterator<String> lit = list.listIterator();

20.3 Set

// HashSet — 無序、不重複,基於雜湊表
Set<String> set = new HashSet<>();
set.add("Apple");
set.add("Banana");
set.add("Apple");              // 不會加入(重複)
set.size();                    // 2
set.contains("Apple");         // true
set.remove("Apple");

// LinkedHashSet — 保持插入順序
Set<String> linkedSet = new LinkedHashSet<>();

// TreeSet — 自然排序或自訂排序
Set<String> treeSet = new TreeSet<>();
treeSet.add("Charlie");
treeSet.add("Alice");
treeSet.add("Bob");
// 走訪順序:Alice, Bob, Charlie

// 不可變 Set(Java 9+)
Set<String> immutableSet = Set.of("a", "b", "c");

// 集合運算
Set<Integer> a = new HashSet<>(Set.of(1, 2, 3, 4));
Set<Integer> b = Set.of(3, 4, 5, 6);

// 交集
Set<Integer> intersection = new HashSet<>(a);
intersection.retainAll(b);     // {3, 4}

// 聯集
Set<Integer> union = new HashSet<>(a);
union.addAll(b);               // {1, 2, 3, 4, 5, 6}

// 差集
Set<Integer> diff = new HashSet<>(a);
diff.removeAll(b);             // {1, 2}

20.4 Map

// HashMap — 無序鍵值對
Map<String, Integer> map = new HashMap<>();
map.put("Alice", 90);
map.put("Bob", 85);
map.put("Charlie", 92);

map.get("Alice");              // 90
map.getOrDefault("Dave", 0);   // 0
map.containsKey("Bob");        // true
map.containsValue(85);         // true
map.remove("Bob");
map.size();                    // 元素數量

// 走訪
for (Map.Entry<String, Integer> entry : map.entrySet()) {
    System.out.println(entry.getKey() + ": " + entry.getValue());
}

map.forEach((key, value) -> System.out.println(key + ": " + value));

for (String key : map.keySet()) { }
for (Integer value : map.values()) { }

// 進階操作(Java 8+)
map.putIfAbsent("Dave", 70);
map.computeIfAbsent("Eve", k -> k.length() * 10);
map.computeIfPresent("Alice", (k, v) -> v + 5);
map.compute("Alice", (k, v) -> v == null ? 0 : v + 1);
map.merge("Alice", 10, Integer::sum);
map.replaceAll((k, v) -> v + 5);

// LinkedHashMap — 保持插入順序
Map<String, Integer> linkedMap = new LinkedHashMap<>();

// TreeMap — 依鍵排序
Map<String, Integer> treeMap = new TreeMap<>();

// 不可變 Map(Java 9+)
Map<String, Integer> immutableMap = Map.of("a", 1, "b", 2);
Map<String, Integer> immutableMap2 = Map.ofEntries(
    Map.entry("a", 1),
    Map.entry("b", 2),
    Map.entry("c", 3)
);

20.5 Queue 與 Deque

// Queue — FIFO
Queue<String> queue = new LinkedList<>();
queue.offer("A");              // 加入尾端
queue.offer("B");
queue.peek();                  // 查看頭部(不移除)"A"
queue.poll();                  // 取出頭部(移除)"A"

// PriorityQueue — 優先佇列(小的先出)
Queue<Integer> pq = new PriorityQueue<>();
pq.offer(30);
pq.offer(10);
pq.offer(20);
pq.poll();                     // 10(最小的先出)

// 自訂排序
Queue<Integer> maxPQ = new PriorityQueue<>(Comparator.reverseOrder());

// Deque — 雙端佇列
Deque<String> deque = new ArrayDeque<>();
deque.offerFirst("A");
deque.offerLast("B");
deque.peekFirst();             // "A"
deque.peekLast();              // "B"
deque.pollFirst();             // "A"
deque.pollLast();              // "B"

// 用 Deque 當作 Stack
Deque<Integer> stack = new ArrayDeque<>();
stack.push(1);
stack.push(2);
stack.push(3);
stack.peek();                  // 3
stack.pop();                   // 3

20.6 Collections 工具類別

List<Integer> list = new ArrayList<>(List.of(3, 1, 4, 1, 5, 9));

Collections.sort(list);                         // 排序
Collections.reverse(list);                       // 反轉
Collections.shuffle(list);                       // 隨機排列
Collections.swap(list, 0, 1);                    // 交換位置
Collections.rotate(list, 2);                     // 旋轉
Collections.frequency(list, 1);                  // 出現次數
Collections.min(list);                           // 最小值
Collections.max(list);                           // 最大值
int idx = Collections.binarySearch(list, 4);     // 二元搜尋(需先排序)

// 不可變包裝
List<String> unmodifiable = Collections.unmodifiableList(list2);
Map<String, Integer> unmodifiableMap = Collections.unmodifiableMap(map);

// 同步包裝
List<String> syncList = Collections.synchronizedList(new ArrayList<>());

// 空集合
List<String> emptyList = Collections.emptyList();
Map<String, String> emptyMap = Collections.emptyMap();

// 單元素集合
List<String> singleton = Collections.singletonList("only");

21. Lambda 表達式與函數式介面

21.1 Lambda 基本語法

// 語法:(參數) -> { 主體 }

// 無參數
Runnable r = () -> System.out.println("Hello");

// 單一參數(可省略括號)
Consumer<String> c = s -> System.out.println(s);

// 多個參數
Comparator<Integer> comp = (a, b) -> a - b;

// 多行主體
Comparator<String> comp2 = (a, b) -> {
    int lenDiff = a.length() - b.length();
    if (lenDiff != 0) return lenDiff;
    return a.compareTo(b);
};

21.2 函數式介面(Functional Interface)

// 只有一個抽象方法的介面
@FunctionalInterface
public interface Transformer<T, R> {
    R transform(T input);
}

Transformer<String, Integer> toLength = s -> s.length();
int len = toLength.transform("Hello");  // 5

21.3 標準函數式介面(java.util.function)

// Function<T, R> — 接受 T,回傳 R
Function<String, Integer> strlen = String::length;
strlen.apply("Hello");                    // 5
Function<Integer, Integer> doubled = x -> x * 2;
Function<Integer, Integer> composed = strlen.andThen(doubled);
composed.apply("Hello");                  // 10

// BiFunction<T, U, R> — 接受 T 和 U,回傳 R
BiFunction<Integer, Integer, Integer> add = Integer::sum;

// Predicate<T> — 接受 T,回傳 boolean
Predicate<String> isLong = s -> s.length() > 5;
isLong.test("Hello World");              // true
Predicate<String> startsWithH = s -> s.startsWith("H");
isLong.and(startsWithH).test("Hello World"); // true
isLong.or(startsWithH).test("Hi");           // true
isLong.negate().test("Hi");                  // true

// Consumer<T> — 接受 T,無回傳
Consumer<String> printer = System.out::println;
printer.accept("Hello");

// Supplier<T> — 無參數,回傳 T
Supplier<Double> random = Math::random;
random.get();

// UnaryOperator<T> — Function<T, T> 的特殊化
UnaryOperator<String> toUpper = String::toUpperCase;

// BinaryOperator<T> — BiFunction<T, T, T> 的特殊化
BinaryOperator<Integer> sum = Integer::sum;

// 基本型別特化版本(避免裝箱拆箱)
IntFunction<String> intToStr = Integer::toString;
IntPredicate isPositive = n -> n > 0;
IntConsumer intPrinter = System.out::println;
IntSupplier randomInt = () -> (int)(Math.random() * 100);
IntUnaryOperator doubleIt = n -> n * 2;
IntBinaryOperator intSum = Integer::sum;
// 同理有 LongXxx, DoubleXxx 版本

21.4 方法參考(Method Reference)

// 靜態方法參考
Function<String, Integer> parseInt = Integer::parseInt;

// 實例方法參考(特定物件)
String str = "Hello";
Supplier<Integer> getLen = str::length;

// 實例方法參考(任意物件)
Function<String, String> toUpper = String::toUpperCase;

// 建構子參考
Supplier<ArrayList<String>> newList = ArrayList::new;
Function<Integer, int[]> newArray = int[]::new;
Function<String, Person> createPerson = Person::new;

// 陣列建構子參考
IntFunction<String[]> arrayCreator = String[]::new;
String[] arr = arrayCreator.apply(5);

21.5 有效的 final 變數

String prefix = "Hello";  // 有效的 final(雖然沒加 final,但沒被修改)

Consumer<String> greeter = name -> {
    System.out.println(prefix + ", " + name);
    // prefix = "Hi";  // 編譯錯誤!Lambda 中使用的區域變數必須是 final 或有效 final
};

22. Stream API

22.1 建立 Stream

// 從集合
List<String> list = List.of("a", "b", "c");
Stream<String> stream = list.stream();
Stream<String> parallelStream = list.parallelStream();

// 從陣列
String[] arr = {"a", "b", "c"};
Stream<String> arrStream = Arrays.stream(arr);

// 從值
Stream<String> values = Stream.of("a", "b", "c");

// 空 Stream
Stream<String> empty = Stream.empty();

// 無限 Stream
Stream<Integer> infinite = Stream.iterate(0, n -> n + 2);   // 0, 2, 4, 6, ...
Stream<Integer> bounded = Stream.iterate(0, n -> n < 100, n -> n + 2); // Java 9+
Stream<Double> randoms = Stream.generate(Math::random);

// 基本型別 Stream
IntStream intStream = IntStream.range(1, 10);      // 1 ~ 9
IntStream intStreamClosed = IntStream.rangeClosed(1, 10); // 1 ~ 10
LongStream longStream = LongStream.of(1L, 2L, 3L);
DoubleStream doubleStream = DoubleStream.of(1.0, 2.0);

// 從字串
IntStream chars = "Hello".chars();

// Stream.ofNullable(Java 9+)
Stream<String> nullable = Stream.ofNullable(getValue()); // 值為 null 則空 Stream

22.2 中間操作(Intermediate Operations)

List<String> names = List.of("Alice", "Bob", "Charlie", "David", "Eve");

// filter — 過濾
names.stream()
     .filter(n -> n.length() > 3)    // ["Alice", "Charlie", "David"]

// map — 轉換
names.stream()
     .map(String::toUpperCase)       // ["ALICE", "BOB", ...]

// flatMap — 扁平化
List<List<Integer>> nested = List.of(List.of(1, 2), List.of(3, 4));
nested.stream()
      .flatMap(Collection::stream)   // [1, 2, 3, 4]

// distinct — 去重
Stream.of(1, 2, 2, 3, 3).distinct()  // [1, 2, 3]

// sorted — 排序
names.stream().sorted()                          // 自然排序
names.stream().sorted(Comparator.reverseOrder()) // 反向排序
names.stream().sorted(Comparator.comparingInt(String::length))

// peek — 偷看(通常用於除錯)
names.stream()
     .peek(n -> System.out.println("Processing: " + n))
     .filter(n -> n.length() > 3)
     .collect(Collectors.toList());

// limit — 限制數量
names.stream().limit(3)              // 前 3 個

// skip — 跳過
names.stream().skip(2)               // 跳過前 2 個

// takeWhile / dropWhile(Java 9+)
Stream.of(1, 2, 3, 4, 5, 1, 2)
      .takeWhile(n -> n < 4)        // [1, 2, 3]
Stream.of(1, 2, 3, 4, 5, 1, 2)
      .dropWhile(n -> n < 4)        // [4, 5, 1, 2]

// mapMulti(Java 16+)
Stream.of(1, 2, 3)
      .<String>mapMulti((n, consumer) -> {
          consumer.accept("Number: " + n);
          consumer.accept("Doubled: " + (n * 2));
      });

22.3 終端操作(Terminal Operations)

List<Integer> numbers = List.of(1, 2, 3, 4, 5);

// forEach — 走訪
numbers.stream().forEach(System.out::println);

// collect — 收集
List<String> result = names.stream()
    .filter(n -> n.length() > 3)
    .collect(Collectors.toList());

// toList()(Java 16+)— 產生不可變 List
List<String> result2 = names.stream()
    .filter(n -> n.length() > 3)
    .toList();

// toArray — 轉陣列
String[] arr = names.stream().toArray(String[]::new);

// reduce — 歸約
int sum = numbers.stream().reduce(0, Integer::sum);
Optional<Integer> max = numbers.stream().reduce(Integer::max);

// count — 計數
long count = names.stream().filter(n -> n.length() > 3).count();

// min / max
Optional<String> shortest = names.stream().min(Comparator.comparingInt(String::length));

// findFirst / findAny
Optional<String> first = names.stream().filter(n -> n.startsWith("A")).findFirst();

// anyMatch / allMatch / noneMatch
boolean hasLong = names.stream().anyMatch(n -> n.length() > 5);
boolean allShort = names.stream().allMatch(n -> n.length() < 10);
boolean noEmpty = names.stream().noneMatch(String::isEmpty);

// 基本型別 Stream 的特殊終端操作
IntStream.rangeClosed(1, 100).sum();
IntStream.rangeClosed(1, 100).average();  // OptionalDouble
IntStream.rangeClosed(1, 100).summaryStatistics(); // count, sum, min, max, avg

22.4 Collectors

List<Person> people = List.of(
    new Person("Alice", 30),
    new Person("Bob", 25),
    new Person("Charlie", 35),
    new Person("David", 25)
);

// 收集為 List / Set
List<String> nameList = people.stream()
    .map(Person::getName)
    .collect(Collectors.toList());

Set<Integer> ages = people.stream()
    .map(Person::getAge)
    .collect(Collectors.toSet());

// 收集為 Map
Map<String, Integer> nameAge = people.stream()
    .collect(Collectors.toMap(Person::getName, Person::getAge));

// 連接字串
String joined = people.stream()
    .map(Person::getName)
    .collect(Collectors.joining(", ", "[", "]"));  // [Alice, Bob, Charlie, David]

// 分組
Map<Integer, List<Person>> byAge = people.stream()
    .collect(Collectors.groupingBy(Person::getAge));

// 分區
Map<Boolean, List<Person>> partitioned = people.stream()
    .collect(Collectors.partitioningBy(p -> p.getAge() > 28));

// 統計
IntSummaryStatistics stats = people.stream()
    .collect(Collectors.summarizingInt(Person::getAge));

// 下游收集器
Map<Integer, Long> countByAge = people.stream()
    .collect(Collectors.groupingBy(Person::getAge, Collectors.counting()));

Map<Integer, List<String>> namesByAge = people.stream()
    .collect(Collectors.groupingBy(
        Person::getAge,
        Collectors.mapping(Person::getName, Collectors.toList())
    ));

// 收集為不可變集合(Java 10+)
List<String> unmodifiable = people.stream()
    .map(Person::getName)
    .collect(Collectors.toUnmodifiableList());

// teeing(Java 12+)— 同時使用兩個收集器
var result = people.stream()
    .collect(Collectors.teeing(
        Collectors.minBy(Comparator.comparingInt(Person::getAge)),
        Collectors.maxBy(Comparator.comparingInt(Person::getAge)),
        (min, max) -> "Youngest: " + min.orElse(null) +
                      ", Oldest: " + max.orElse(null)
    ));

22.5 Optional

// 建立
Optional<String> opt1 = Optional.of("Hello");         // 非 null
Optional<String> opt2 = Optional.ofNullable(null);     // 可能 null
Optional<String> opt3 = Optional.empty();               // 空

// 檢查與取值
opt1.isPresent();                  // true
opt1.isEmpty();                    // false(Java 11+)
opt1.get();                        // "Hello"(空的話拋 NoSuchElementException)

// 安全取值
opt1.orElse("default");
opt1.orElseGet(() -> computeDefault());
opt1.orElseThrow();                                    // Java 10+
opt1.orElseThrow(() -> new RuntimeException("Not found"));

// 條件操作
opt1.ifPresent(System.out::println);
opt1.ifPresentOrElse(                                  // Java 9+
    System.out::println,
    () -> System.out.println("Empty")
);

// 轉換
Optional<Integer> len = opt1.map(String::length);      // Optional[5]
Optional<String> upper = opt1.flatMap(s ->
    s.isEmpty() ? Optional.empty() : Optional.of(s.toUpperCase())
);

// 過濾
Optional<String> filtered = opt1.filter(s -> s.length() > 3);

// or(Java 9+)
Optional<String> alt = opt3.or(() -> Optional.of("alternative"));

// stream(Java 9+)
Stream<String> stream = opt1.stream();  // 有值則 Stream 含一個元素

23. I/O 與 NIO

23.1 位元組串流(Byte Streams)

// FileInputStream / FileOutputStream
try (FileInputStream fis = new FileInputStream("input.bin");
     FileOutputStream fos = new FileOutputStream("output.bin")) {
    byte[] buffer = new byte[1024];
    int bytesRead;
    while ((bytesRead = fis.read(buffer)) != -1) {
        fos.write(buffer, 0, bytesRead);
    }
}

// BufferedInputStream / BufferedOutputStream
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("file.bin"));
     BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.bin"))) {
    bis.transferTo(bos);  // Java 9+
}

23.2 字元串流(Character Streams)

// FileReader / FileWriter
try (FileReader fr = new FileReader("input.txt");
     FileWriter fw = new FileWriter("output.txt")) {
    int ch;
    while ((ch = fr.read()) != -1) {
        fw.write(ch);
    }
}

// BufferedReader / BufferedWriter
try (BufferedReader br = new BufferedReader(new FileReader("input.txt"));
     BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"))) {
    String line;
    while ((line = br.readLine()) != null) {
        bw.write(line);
        bw.newLine();
    }
}

// PrintWriter
try (PrintWriter pw = new PrintWriter(new FileWriter("output.txt"))) {
    pw.println("Hello");
    pw.printf("Name: %s, Age: %d%n", "Alice", 25);
}

23.3 標準 I/O

// 標準輸出
System.out.println("Hello");     // 含換行
System.out.print("Hello");       // 不含換行
System.out.printf("x = %d%n", 42);

// 標準輸入
Scanner scanner = new Scanner(System.in);
String line = scanner.nextLine();
int num = scanner.nextInt();
double d = scanner.nextDouble();
scanner.close();

// Console(適合密碼輸入)
Console console = System.console();
if (console != null) {
    String user = console.readLine("Username: ");
    char[] pass = console.readPassword("Password: ");
}

23.4 序列化(Serialization)

// 實作 Serializable 介面
public class Employee implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;

    private String name;
    private transient String password;  // transient 不會被序列化
    private int age;
}

// 序列化(寫入)
try (ObjectOutputStream oos = new ObjectOutputStream(
         new FileOutputStream("employee.ser"))) {
    oos.writeObject(new Employee("Alice", "secret", 30));
}

// 反序列化(讀取)
try (ObjectInputStream ois = new ObjectInputStream(
         new FileInputStream("employee.ser"))) {
    Employee emp = (Employee) ois.readObject();
}

23.5 NIO.2(java.nio.file)

// Path
Path path = Path.of("src", "main", "java");
Path path2 = Paths.get("/Users/nelson/file.txt");

path.getFileName();       // "java"
path.getParent();         // "src/main"
path.toAbsolutePath();
path.resolve("App.java"); // "src/main/java/App.java"
path.normalize();          // 正規化路徑

// Files 工具類別
// 讀取
String content = Files.readString(Path.of("file.txt"));
List<String> lines = Files.readAllLines(Path.of("file.txt"));
byte[] bytes = Files.readAllBytes(Path.of("file.bin"));

// 寫入
Files.writeString(Path.of("file.txt"), "Hello World");
Files.write(Path.of("file.txt"), List.of("line1", "line2"));

// 檔案操作
Files.exists(path);
Files.isDirectory(path);
Files.isRegularFile(path);
Files.size(path);
Files.createFile(path);
Files.createDirectories(path);
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
Files.move(source, target, StandardCopyOption.ATOMIC_MOVE);
Files.delete(path);
Files.deleteIfExists(path);

// 走訪目錄
try (Stream<Path> entries = Files.list(Path.of("."))) {
    entries.forEach(System.out::println);
}

try (Stream<Path> tree = Files.walk(Path.of("."), 3)) {
    tree.filter(Files.isRegularFile(p))
        .forEach(System.out::println);
}

try (Stream<Path> found = Files.find(Path.of("."), 10,
         (p, attr) -> p.toString().endsWith(".java"))) {
    found.forEach(System.out::println);
}

// 監視目錄變更
WatchService watcher = FileSystems.getDefault().newWatchService();
Path dir = Path.of("watched");
dir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE,
                      StandardWatchEventKinds.ENTRY_MODIFY);

WatchKey key;
while ((key = watcher.take()) != null) {
    for (WatchEvent<?> event : key.pollEvents()) {
        System.out.println(event.kind() + ": " + event.context());
    }
    key.reset();
}

24. 多執行緒與並行處理

24.1 建立執行緒

// 方法一:繼承 Thread
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread running: " + getName());
    }
}
new MyThread().start();

// 方法二:實作 Runnable
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable running");
    }
}
new Thread(new MyRunnable()).start();

// 方法三:Lambda
new Thread(() -> System.out.println("Lambda thread")).start();

// 方法四:Callable(可回傳值)
Callable<Integer> task = () -> {
    Thread.sleep(1000);
    return 42;
};

24.2 Thread 方法

Thread t = new Thread(() -> {
    try {
        Thread.sleep(2000);         // 暫停 2 秒
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
});

t.start();                           // 啟動執行緒
t.join();                            // 等待執行緒完成
t.join(5000);                        // 最多等待 5 秒
t.isAlive();                         // 是否仍在執行
t.setName("Worker-1");               // 設定名稱
t.setPriority(Thread.MAX_PRIORITY);  // 設定優先順序
t.setDaemon(true);                   // 設為守護執行緒

Thread.currentThread();              // 取得目前執行緒
Thread.sleep(1000);                  // 暫停目前執行緒
Thread.yield();                      // 讓出 CPU 時間

24.3 同步化(Synchronization)

// synchronized 方法
public class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public synchronized int getCount() {
        return count;
    }
}

// synchronized 區塊
public void addItem(String item) {
    synchronized (this) {
        list.add(item);
    }
}

// 使用特定的鎖定物件
private final Object lock = new Object();
public void safeMethod() {
    synchronized (lock) {
        // 臨界區
    }
}

// wait / notify
public class ProducerConsumer {
    private final Queue<Integer> queue = new LinkedList<>();
    private final int capacity = 10;

    public synchronized void produce(int item) throws InterruptedException {
        while (queue.size() == capacity) {
            wait();
        }
        queue.add(item);
        notifyAll();
    }

    public synchronized int consume() throws InterruptedException {
        while (queue.isEmpty()) {
            wait();
        }
        int item = queue.poll();
        notifyAll();
        return item;
    }
}

24.4 Lock API

import java.util.concurrent.locks.*;

// ReentrantLock
private final ReentrantLock lock = new ReentrantLock();

public void safeIncrement() {
    lock.lock();
    try {
        count++;
    } finally {
        lock.unlock();
    }
}

// tryLock
if (lock.tryLock(5, TimeUnit.SECONDS)) {
    try {
        // 取得鎖定
    } finally {
        lock.unlock();
    }
}

// ReadWriteLock
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();

public String read() {
    rwLock.readLock().lock();
    try {
        return data;
    } finally {
        rwLock.readLock().unlock();
    }
}

public void write(String newData) {
    rwLock.writeLock().lock();
    try {
        data = newData;
    } finally {
        rwLock.writeLock().unlock();
    }
}

24.5 Executor 框架

import java.util.concurrent.*;

// 建立執行緒池
ExecutorService fixed = Executors.newFixedThreadPool(4);
ExecutorService cached = Executors.newCachedThreadPool();
ExecutorService single = Executors.newSingleThreadExecutor();
ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(2);

// 提交任務
fixed.execute(() -> System.out.println("Task"));

// 提交有回傳值的任務
Future<Integer> future = fixed.submit(() -> {
    Thread.sleep(1000);
    return 42;
});
Integer result = future.get();             // 阻塞等待結果
Integer result2 = future.get(5, TimeUnit.SECONDS); // 超時等待

// 批次提交
List<Callable<String>> tasks = List.of(
    () -> "Task 1",
    () -> "Task 2",
    () -> "Task 3"
);
List<Future<String>> futures = fixed.invokeAll(tasks);
String fastest = fixed.invokeAny(tasks);   // 回傳最先完成的

// 排程任務
scheduled.schedule(() -> System.out.println("Delayed"), 5, TimeUnit.SECONDS);
scheduled.scheduleAtFixedRate(() -> System.out.println("Periodic"),
    0, 1, TimeUnit.SECONDS);
scheduled.scheduleWithFixedDelay(() -> System.out.println("Fixed delay"),
    0, 1, TimeUnit.SECONDS);

// 關閉
fixed.shutdown();                          // 等待現有任務完成
fixed.shutdownNow();                       // 嘗試中斷所有任務
fixed.awaitTermination(60, TimeUnit.SECONDS);

24.6 CompletableFuture

// 建立
CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> {
    return fetchData();
});

// 串接操作
CompletableFuture<Integer> result = cf
    .thenApply(String::length)             // 轉換
    .thenApply(len -> len * 2);

// 消費結果
cf.thenAccept(System.out::println);

// 處理例外
cf.exceptionally(ex -> "Error: " + ex.getMessage());
cf.handle((value, ex) -> {
    if (ex != null) return "Error";
    return value;
});

// 組合多個 Future
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> "World");

// 兩個都完成
CompletableFuture<String> combined = f1.thenCombine(f2, (a, b) -> a + " " + b);

// 任一完成
CompletableFuture<Object> either = CompletableFuture.anyOf(f1, f2);

// 全部完成
CompletableFuture<Void> all = CompletableFuture.allOf(f1, f2);
all.thenRun(() -> {
    String r1 = f1.join();
    String r2 = f2.join();
});

// 鏈式非同步操作
CompletableFuture.supplyAsync(() -> fetchUserId())
    .thenCompose(id -> CompletableFuture.supplyAsync(() -> fetchUser(id)))
    .thenCompose(user -> CompletableFuture.supplyAsync(() -> fetchOrders(user)))
    .thenAccept(orders -> display(orders))
    .exceptionally(ex -> { log(ex); return null; });

24.7 並行集合

// ConcurrentHashMap
ConcurrentMap<String, Integer> concMap = new ConcurrentHashMap<>();
concMap.put("key", 1);
concMap.computeIfAbsent("key2", k -> 42);

// CopyOnWriteArrayList(讀多寫少場景)
List<String> cowList = new CopyOnWriteArrayList<>();

// BlockingQueue
BlockingQueue<String> queue = new LinkedBlockingQueue<>(100);
queue.put("item");         // 阻塞直到有空間
String item = queue.take(); // 阻塞直到有元素

// ConcurrentLinkedQueue(無鎖定佇列)
Queue<String> clq = new ConcurrentLinkedQueue<>();

24.8 原子操作

import java.util.concurrent.atomic.*;

AtomicInteger atomicInt = new AtomicInteger(0);
atomicInt.incrementAndGet();           // 原子遞增
atomicInt.getAndIncrement();           // 原子遞增(回傳舊值)
atomicInt.compareAndSet(1, 2);         // CAS 操作
atomicInt.addAndGet(5);
atomicInt.updateAndGet(x -> x * 2);
atomicInt.accumulateAndGet(10, Integer::sum);

AtomicReference<String> atomicRef = new AtomicReference<>("Hello");
atomicRef.compareAndSet("Hello", "World");

// LongAdder(高競爭場景效能優於 AtomicLong)
LongAdder adder = new LongAdder();
adder.increment();
adder.add(10);
long sum = adder.sum();

24.9 虛擬執行緒(Virtual Threads, Java 21+)

// 建立虛擬執行緒
Thread vThread = Thread.ofVirtual().start(() -> {
    System.out.println("Virtual thread running");
});

// 使用工廠
ThreadFactory factory = Thread.ofVirtual().name("worker-", 0).factory();

// 使用 ExecutorService
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    IntStream.range(0, 10_000).forEach(i -> {
        executor.submit(() -> {
            Thread.sleep(Duration.ofSeconds(1));
            return i;
        });
    });
}

25. 註解(Annotations)

25.1 內建註解

@Override                    // 覆寫父類別方法
@Deprecated                  // 標記已棄用
@Deprecated(since = "9", forRemoval = true)  // Java 9+
@SuppressWarnings("unchecked")               // 抑制警告
@SuppressWarnings({"unchecked", "rawtypes"})
@SafeVarargs                 // 抑制泛型可變參數警告
@FunctionalInterface         // 標記函數式介面
@Serial                      // 標記序列化相關成員(Java 14+)

25.2 自訂註解

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)      // 保留到執行時期
@Target(ElementType.METHOD)               // 只能用於方法
@Documented                               // 包含在 Javadoc 中
@Inherited                                // 子類別繼承此註解
public @interface MyAnnotation {
    String value() default "";            // 元素(有預設值)
    int priority() default 0;
    String[] tags() default {};
}

// 使用
@MyAnnotation(value = "test", priority = 1, tags = {"a", "b"})
public void myMethod() { }

// 如果只有 value 一個元素,可以省略名稱
@MyAnnotation("test")
public void myMethod2() { }

25.3 元註解

元註解 用途
@Retention 註解保留策略(SOURCE / CLASS / RUNTIME)
@Target 註解可套用的位置(TYPE / METHOD / FIELD / PARAMETER 等)
@Documented 是否包含在 Javadoc
@Inherited 子類別是否繼承
@Repeatable 是否可重複套用

25.4 可重複註解(Java 8+)

@Repeatable(Schedules.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Schedule {
    String dayOfWeek();
    String time();
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Schedules {
    Schedule[] value();
}

// 使用
@Schedule(dayOfWeek = "Mon", time = "09:00")
@Schedule(dayOfWeek = "Fri", time = "15:00")
public void scheduledTask() { }

25.5 執行時期讀取註解(反射)

Method method = MyClass.class.getMethod("myMethod");

if (method.isAnnotationPresent(MyAnnotation.class)) {
    MyAnnotation ann = method.getAnnotation(MyAnnotation.class);
    System.out.println("Value: " + ann.value());
    System.out.println("Priority: " + ann.priority());
}

// 取得所有註解
Annotation[] annotations = method.getAnnotations();

26. 套件與模組系統

26.1 套件(Package)

// 宣告套件(檔案第一行)
package com.example.myapp;

// 匯入
import java.util.List;
import java.util.ArrayList;
import java.util.*;                     // 匯入整個套件(不建議)
import static java.lang.Math.PI;       // 靜態匯入
import static java.lang.Math.*;        // 靜態匯入所有

// 目錄結構必須符合套件結構
// com/example/myapp/MyClass.java

26.2 模組系統(Java 9+, JPMS)

// module-info.java(模組描述檔)
module com.example.myapp {
    requires java.sql;                  // 依賴其他模組
    requires transitive java.logging;   // 傳遞性依賴

    exports com.example.myapp.api;      // 公開套件
    exports com.example.myapp.spi to    // 限定公開
            com.example.plugin;

    opens com.example.myapp.model;      // 開放反射存取
    opens com.example.myapp.model to    // 限定開放
            com.fasterxml.jackson.databind;

    provides com.example.spi.Service    // 提供服務實作
            with com.example.myapp.ServiceImpl;

    uses com.example.spi.Service;       // 使用服務
}

27. Record 類別(Java 14+)

27.1 基本 Record

// Record 自動產生:建構子、getter、equals、hashCode、toString
public record Point(int x, int y) { }

Point p = new Point(10, 20);
System.out.println(p.x());       // 10(getter 沒有 get 前綴)
System.out.println(p.y());       // 20
System.out.println(p);           // Point[x=10, y=20]

Point p2 = new Point(10, 20);
System.out.println(p.equals(p2)); // true

27.2 自訂 Record

public record Range(int start, int end) {
    // 精簡建構子(Compact Constructor)— 驗證用
    public Range {
        if (start > end) {
            throw new IllegalArgumentException("start must be <= end");
        }
    }

    // 自訂方法
    public int length() {
        return end - start;
    }

    // 靜態方法
    public static Range of(int start, int end) {
        return new Range(start, end);
    }

    // 可以實作介面
}

// Record 可以實作介面
public record NamedPoint(String name, int x, int y) implements Serializable { }

// Record 是 final 的,不能被繼承
// Record 的成員變數都是 final 的
// Record 不能有實例變數(除了 Record 元件)
// Record 可以有靜態變數和方法

28. 密封類別(Sealed Classes, Java 17+)

28.1 基本密封類別

// 限制哪些類別可以繼承
public sealed class Shape
    permits Circle, Rectangle, Triangle {

    public abstract double area();
}

public final class Circle extends Shape {
    private final double radius;
    public Circle(double radius) { this.radius = radius; }
    @Override public double area() { return Math.PI * radius * radius; }
}

public final class Rectangle extends Shape {
    private final double width, height;
    public Rectangle(double w, double h) { this.width = w; this.height = h; }
    @Override public double area() { return width * height; }
}

public non-sealed class Triangle extends Shape {
    // non-sealed 允許任意子類別繼承
    private final double base, height;
    public Triangle(double b, double h) { this.base = b; this.height = h; }
    @Override public double area() { return 0.5 * base * height; }
}

28.2 密封介面

public sealed interface Expr
    permits Literal, Add, Multiply, Negate {
}

public record Literal(double value) implements Expr { }
public record Add(Expr left, Expr right) implements Expr { }
public record Multiply(Expr left, Expr right) implements Expr { }
public record Negate(Expr operand) implements Expr { }

29. 模式比對(Pattern Matching)

29.1 instanceof 模式比對(Java 16+)

Object obj = "Hello";

if (obj instanceof String s) {
    System.out.println(s.toUpperCase());
}

if (obj instanceof String s && s.length() > 3) {
    System.out.println(s);
}

29.2 switch 模式比對(Java 21+)

// 型別模式
String describe(Object obj) {
    return switch (obj) {
        case Integer i -> "Integer: " + i;
        case Long l    -> "Long: " + l;
        case String s  -> "String: " + s;
        case int[] a   -> "Array of length " + a.length;
        case null      -> "null";
        default        -> "Unknown";
    };
}

// 守衛條件
String categorize(Object obj) {
    return switch (obj) {
        case Integer i when i < 0 -> "Negative";
        case Integer i when i == 0 -> "Zero";
        case Integer i -> "Positive";
        case String s when s.isEmpty() -> "Empty string";
        case String s -> "String: " + s;
        default -> "Other";
    };
}

// Record 模式(Java 21+)
sealed interface Shape permits Circle2, Rect2 {}
record Circle2(double radius) implements Shape {}
record Rect2(double width, double height) implements Shape {}

double area(Shape shape) {
    return switch (shape) {
        case Circle2(var r)    -> Math.PI * r * r;
        case Rect2(var w, var h) -> w * h;
    };
}

// 巢狀 Record 解構
record Point2(int x, int y) {}
record Line(Point2 start, Point2 end) {}

String describeLine(Line line) {
    return switch (line) {
        case Line(Point2(var x1, var y1), Point2(var x2, var y2))
            when x1 == x2 -> "Vertical line at x=" + x1;
        case Line(Point2(var x1, var y1), Point2(var x2, var y2))
            when y1 == y2 -> "Horizontal line at y=" + y1;
        case Line(var start, var end) -> "Line from " + start + " to " + end;
    };
}

30. 其他實用語法

30.1 正規表達式

import java.util.regex.*;

// 基本比對
boolean matches = "Hello123".matches("\\w+\\d+");

// Pattern 與 Matcher
Pattern pattern = Pattern.compile("(\\d{4})-(\\d{2})-(\\d{2})");
Matcher matcher = pattern.matcher("2024-01-15");

if (matcher.matches()) {
    String year = matcher.group(1);    // "2024"
    String month = matcher.group(2);   // "01"
    String day = matcher.group(3);     // "15"
}

// 尋找所有匹配
Pattern wordPattern = Pattern.compile("\\b\\w+\\b");
Matcher wordMatcher = wordPattern.matcher("Hello World Java");
while (wordMatcher.find()) {
    System.out.println(wordMatcher.group());
}

// Stream(Java 9+)
Pattern.compile(",")
       .splitAsStream("a,b,c,d")
       .forEach(System.out::println);

// 命名群組
Pattern named = Pattern.compile("(?<year>\\d{4})-(?<month>\\d{2})");
Matcher m = named.matcher("2024-01");
if (m.matches()) {
    String year = m.group("year");
}

30.2 日期與時間(java.time, Java 8+)

import java.time.*;
import java.time.format.*;
import java.time.temporal.*;

// 日期
LocalDate today = LocalDate.now();
LocalDate date = LocalDate.of(2024, 1, 15);
LocalDate parsed = LocalDate.parse("2024-01-15");

date.getYear();                  // 2024
date.getMonth();                 // JANUARY
date.getDayOfMonth();            // 15
date.getDayOfWeek();             // MONDAY
date.plusDays(10);
date.minusMonths(1);
date.isLeapYear();
date.lengthOfMonth();

// 時間
LocalTime now = LocalTime.now();
LocalTime time = LocalTime.of(14, 30, 0);
time.getHour();                  // 14
time.plusHours(2);

// 日期時間
LocalDateTime dateTime = LocalDateTime.now();
LocalDateTime dt = LocalDateTime.of(2024, 1, 15, 14, 30);
LocalDateTime dt2 = LocalDateTime.of(date, time);

// 帶時區
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Asia/Taipei"));
ZonedDateTime utc = ZonedDateTime.now(ZoneOffset.UTC);

// 時間戳
Instant instant = Instant.now();
long epochSecond = instant.getEpochSecond();
long epochMilli = instant.toEpochMilli();

// 時間間隔
Duration duration = Duration.between(time1, time2);
duration.toHours();
duration.toMinutes();
Duration fiveMinutes = Duration.ofMinutes(5);

Period period = Period.between(date1, date2);
period.getYears();
period.getMonths();
period.getDays();

// 格式化
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
String formatted = dateTime.format(formatter);
LocalDateTime parsedDt = LocalDateTime.parse("2024/01/15 14:30:00", formatter);

// 預定義格式
DateTimeFormatter.ISO_LOCAL_DATE;       // 2024-01-15
DateTimeFormatter.ISO_LOCAL_DATE_TIME;  // 2024-01-15T14:30:00

// 時間調整
LocalDate firstDayOfMonth = date.with(TemporalAdjusters.firstDayOfMonth());
LocalDate nextMonday = date.with(TemporalAdjusters.next(DayOfWeek.MONDAY));

30.3 數學運算(Math)

Math.abs(-5);          // 5
Math.max(3, 7);        // 7
Math.min(3, 7);        // 3
Math.pow(2, 10);       // 1024.0
Math.sqrt(16);         // 4.0
Math.cbrt(27);         // 3.0
Math.log(Math.E);      // 1.0
Math.log10(100);       // 2.0
Math.ceil(3.2);        // 4.0
Math.floor(3.8);       // 3.0
Math.round(3.5);       // 4
Math.random();         // [0.0, 1.0)

Math.PI;               // 3.141592653589793
Math.E;                // 2.718281828459045

// 溢位安全運算(Java 8+)
Math.addExact(Integer.MAX_VALUE, 1);   // 拋出 ArithmeticException
Math.multiplyExact(Integer.MAX_VALUE, 2);

30.4 比較器(Comparator)

// 自訂比較器
Comparator<String> byLength = (a, b) -> Integer.compare(a.length(), b.length());

// 使用工廠方法
Comparator<String> byLength2 = Comparator.comparingInt(String::length);
Comparator<Person> byAge = Comparator.comparingInt(Person::getAge);
Comparator<Person> byName = Comparator.comparing(Person::getName);

// 組合比較器
Comparator<Person> byAgeThenName = Comparator
    .comparingInt(Person::getAge)
    .thenComparing(Person::getName);

// 反向
Comparator<Person> byAgeDesc = Comparator
    .comparingInt(Person::getAge)
    .reversed();

// null 處理
Comparator<String> nullSafe = Comparator.nullsFirst(Comparator.naturalOrder());
Comparator<String> nullLast = Comparator.nullsLast(Comparator.naturalOrder());

30.5 Iterable 與 Iterator

// 自訂可迭代類別
public class NumberRange implements Iterable<Integer> {
    private final int start;
    private final int end;

    public NumberRange(int start, int end) {
        this.start = start;
        this.end = end;
    }

    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<>() {
            private int current = start;

            @Override
            public boolean hasNext() { return current <= end; }

            @Override
            public Integer next() {
                if (!hasNext()) throw new NoSuchElementException();
                return current++;
            }
        };
    }
}

// 可以使用 for-each
for (int n : new NumberRange(1, 10)) {
    System.out.println(n);
}

30.6 反射(Reflection)

// 取得 Class 物件
Class<?> clazz = String.class;
Class<?> clazz2 = "Hello".getClass();
Class<?> clazz3 = Class.forName("java.lang.String");

// 檢查類別資訊
clazz.getName();               // "java.lang.String"
clazz.getSimpleName();         // "String"
clazz.getSuperclass();         // Object.class
clazz.getInterfaces();
clazz.getModifiers();
clazz.isInterface();
clazz.isEnum();
clazz.isRecord();

// 取得成員
Field[] fields = clazz.getDeclaredFields();
Method[] methods = clazz.getDeclaredMethods();
Constructor<?>[] constructors = clazz.getDeclaredConstructors();

// 建立實例
Constructor<?> ctor = clazz.getDeclaredConstructor(String.class);
Object instance = ctor.newInstance("Hello");

// 存取私有成員
Field field = clazz.getDeclaredField("value");
field.setAccessible(true);
Object value = field.get(instance);

// 呼叫方法
Method method = clazz.getDeclaredMethod("length");
int length = (int) method.invoke(instance);

30.7 var 模式(Unnamed Patterns, Java 22+)

// 未命名變數(_)— 不需要使用的變數
try {
    // ...
} catch (Exception _) {
    System.out.println("Error occurred");
}

for (var _ : list) {
    count++;
}

map.forEach((_, value) -> System.out.println(value));

30.8 字串範本(String Templates, Preview in Java 21+)

// 注意:此功能在不同 Java 版本間可能有變化
// Java 21 預覽版使用 STR 處理器
String name = "World";
int x = 10;
// String msg = STR."Hello, \{name}! x = \{x}";

// 在正式功能前,建議使用 String.format 或 formatted
String msg = "Hello, %s! x = %d".formatted(name, x);

30.9 斷言(Assertions)

// 基本斷言
assert x > 0;
assert x > 0 : "x must be positive, but was " + x;

// 需要啟用才有效(預設停用)
// java -ea MyApp          啟用所有斷言
// java -ea:com.example... 啟用特定套件
// java -da MyApp          停用斷言

附錄 A:常用設計模式

單例模式(Singleton)

public class Singleton {
    private static final Singleton INSTANCE = new Singleton();
    private Singleton() { }
    public static Singleton getInstance() { return INSTANCE; }
}

// 使用 enum 實現(推薦)
public enum SingletonEnum {
    INSTANCE;
    public void doSomething() { }
}

建造者模式(Builder)

public class User {
    private final String name;
    private final int age;
    private final String email;
    private final String phone;

    private User(Builder builder) {
        this.name = builder.name;
        this.age = builder.age;
        this.email = builder.email;
        this.phone = builder.phone;
    }

    public static class Builder {
        private final String name;  // 必要
        private int age;
        private String email;
        private String phone;

        public Builder(String name) {
            this.name = name;
        }

        public Builder age(int age) { this.age = age; return this; }
        public Builder email(String email) { this.email = email; return this; }
        public Builder phone(String phone) { this.phone = phone; return this; }

        public User build() {
            return new User(this);
        }
    }
}

User user = new User.Builder("Alice")
    .age(25)
    .email("alice@example.com")
    .build();

觀察者模式(Observer)

public interface Observer {
    void update(String event, Object data);
}

public class EventBus {
    private final Map<String, List<Observer>> listeners = new HashMap<>();

    public void subscribe(String event, Observer observer) {
        listeners.computeIfAbsent(event, k -> new ArrayList<>()).add(observer);
    }

    public void publish(String event, Object data) {
        List<Observer> observers = listeners.getOrDefault(event, List.of());
        observers.forEach(o -> o.update(event, data));
    }
}

附錄 B:Java 版本重要特性摘要

版本 重要特性
Java 8 Lambda、Stream API、Optional、新日期時間 API、Default Method
Java 9 模組系統(JPMS)、JShell、私有介面方法、不可變集合工廠方法
Java 10 var 區域變數型別推斷
Java 11 (LTS) String 新方法、HttpClientvar in Lambda
Java 12 Switch 表達式(預覽)
Java 13 文字區塊(預覽)
Java 14 Record(預覽)、instanceof 模式比對(預覽)、Switch 表達式(正式)
Java 15 密封類別(預覽)、文字區塊(正式)
Java 16 Record(正式)、instanceof 模式比對(正式)、Stream.toList()
Java 17 (LTS) 密封類別(正式)、模式比對 switch(預覽)
Java 18 簡單 Web 伺服器
Java 19 虛擬執行緒(預覽)、結構化並行(預覽)
Java 20 作用域值(預覽)
Java 21 (LTS) 虛擬執行緒(正式)、模式比對 switch(正式)、Record 模式(正式)、序列集合
Java 22 未命名變數 _(正式)、字串範本(第二次預覽)

最後更新:2026 年 4 月

本教材涵蓋 Java SE 核心語法。如需更深入的主題(如 JDBC、網路程式設計、設計模式等),請參閱 Oracle 官方文件或相關進階書籍。