舊 API (Java 7 及之前):java.text.DateFormat、SimpleDateFormat — **非執行緒安全**、設計缺陷多、時區處理麻煩。
現代 API (Java 8+):java.time 套件 — **不可變、執行緒安全、領域驅動設計、ISO-8601 標準支援**。
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class ModernDateFormat {
public static void main(String[] args) {
LocalDateTime now = LocalDateTime.now();
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Asia/Taipei"));
Instant instant = Instant.now();
// 預定義格式
System.out.println("ISO_LOCAL_DATE_TIME: " + now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
System.out.println("ISO_ZONED_DATE_TIME: " + zdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
System.out.println("RFC_1123_DATE_TIME: " + zdt.format(DateTimeFormatter.RFC_1123_DATE_TIME));
// 自訂格式
DateTimeFormatter zhTw = DateTimeFormatter.ofPattern("yyyy年MM月dd日 EEEE", Locale.TRADITIONAL_CHINESE);
DateTimeFormatter custom = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS z");
System.out.println("中文完整: " + zdt.format(zhTw)); // 2024年01月15日 星期一
System.out.println("自訂含時區: " + zdt.format(custom)); // 2024-01-15 14:30:45.123 CST
// 解析字串
LocalDate parsed = LocalDate.parse("2024/01/15", DateTimeFormatter.ofPattern("yyyy/MM/dd"));
System.out.println("解析結果: " + parsed);
}
}
| 舊類別 | 現代替代 | 關鍵差異 |
|---|---|---|
java.util.Date | Instant (時間戳) / ZonedDateTime (帶時區) | 不可變、奈秒精度、時區明確 |
java.util.Calendar | ZonedDateTime / LocalDateTime | 流暢 API、不可變、無偏移月份陷阱 |
SimpleDateFormat | DateTimeFormatter | 執行緒安全、可快取重用、支援區域化 |
TimeZone | ZoneId / ZoneOffset | IANA 時區資料庫、歷史變更支援 |
⚠️ 嚴禁 在多執行緒環境共用
SimpleDateFormat實例。若必須維護舊代碼,使用ThreadLocal<SimpleDateFormat>或每次new SimpleDateFormat(...)。
| 特性 | 靜態巢狀類別 static class Inner | 內部類別 (非靜態) class Inner |
|---|---|---|
| 外部類別實例關聯 | **無** — 可獨立實例化 | **隱含持有** 外部實例引用 (Outer.this) |
| 存取外部成員 | 僅能存取 static 成員 | 可存取**所有** 外部成員 (含私有) |
| 實例化語法 | new Outer.Inner() | outerInstance.new Inner() |
| 序列化風險 | 無隱含引用,安全 | 序列化外部實例會連帶序列化內部實例,易造成記憶體洩漏/NotSerializableException |
| 典型用途 | 工具類、Builder、資料載體、獨立邏輯單元 | 迭代器、事件監聽器、需強綁定外部狀態的邏輯 |
public class Order {
private final String id;
private final List items = new ArrayList<>();
// 靜態巢狀類別:Builder 不需持有 Order 實例
public static class Builder {
private String id;
private final List items = new ArrayList<>();
public Builder id(String id) { this.id = id; return this; }
public Builder addItem(OrderItem item) { items.add(item); return this; }
public Order build() { return new Order(this); }
}
// 非靜態內部類別:迭代器需存取 Order 私有 items
private class OrderIterator implements Iterator {
private int index = 0;
public boolean hasNext() { return index < items.size(); }
public OrderItem next() { return items.get(index++); }
}
private Order(Builder b) { this.id = b.id; this.items.addAll(b.items); }
public Iterator iterator() { return new OrderIterator(); }
}
| 方法 | 適用型別 | 效能 | 特點 | 範例 |
|---|---|---|---|---|
Integer.parseInt(s) / Long.parseLong(s) / Double.parseDouble(s) | 整數/長整數/浮點數 | 高 (原生) | **標準做法**,非數字拋 NumberFormatException,需 try-catch | try { Long.parseLong(s); return true; } catch (NumberFormatException e) { return false; } |
Character.isDigit(char) | 單一字元 | 極高 | 僅判斷 0-9,**不支援負號/小數點/科學記號** | s.chars().allMatch(Character::isDigit) (全數字) |
String.matches("-?\\d+(\\.\\d+)?") | 字串 | 中 (正則) | 可自訂格式 (整數/浮點/科學記號),編譯正則有開銷 | Pattern.compile("^-?\\d+(\\.\\d+)?$").matcher(s).matches() |
Scanner.hasNextInt() / hasNextDouble() | 輸入流 | 低 | 掃描器語意,適合解析輸入流,非字串判斷首選 | new Scanner(s).hasNextInt() |
Apache Commons Lang NumberUtils.isCreatable(s) / isParsable(s) | 字串 | 中 | 支援十六進位、科學記號、型別後綴 (L/F/D),工具類豐富 | NumberUtils.isParsable(s) (嚴格) |
public final class NumberValidator {
private static final Pattern INTEGER_PATTERN = Pattern.compile("^-?\\d+$");
private static final Pattern DECIMAL_PATTERN = Pattern.compile("^-?\\d+(\\.\\d+)?$");
public static boolean isInteger(String s) {
return s != null && INTEGER_PATTERN.matcher(s).matches();
}
public static boolean isDecimal(String s) {
return s != null && DECIMAL_PATTERN.matcher(s).matches();
}
// 高效能整數解析 (避免例外開銷)
public static OptionalLong parseLong(String s) {
if (s == null || s.isEmpty()) return OptionalLong.empty();
int i = 0, sign = 1;
if (s.charAt(0) == '-') { sign = -1; i = 1; }
else if (s.charAt(0) == '+') { i = 1; }
long val = 0;
for (; i < s.length(); i++) {
char c = s.charAt(i);
if (c < '0' || c > '9') return OptionalLong.empty();
val = val * 10 + (c - '0');
}
return OptionalLong.of(val * sign);
}
}
| 維度 | finalize() | System.gc() |
|---|---|---|
| 本質 | 物件方法,GC 執行前由 JVM **自動呼叫** (單次、單執行緒) | 靜態方法,**建議** JVM 執行 GC,**不保證** 立即/必定執行 |
| 用途 | 釋放非堆資源 (檔案控制碼、原生記憶體、JNI 參考) — **已棄用 (JDK 9)** | 測試/診斷/特定時機強制回收 (如基準測試前) |
| 執行時機 | 物件成為不可達 → GC 判定 → 放入 Finalizer 佇列 → Finalizer 執行緒呼叫 | 呼叫即觸發 GC 請求,JVM 視記憶體壓力決定是否執行 |
| 可靠性 | **極低**:可能延遲無限久、可能永不執行、異常會被吞噬、拖慢 GC、導致物件復活 | **無保證**:僅是建議,JVM 可忽略 (如 -XX:+DisableExplicitGC) |
| 現代替代 | try-with-resources / AutoCloseable / Cleaner (JDK 9+) / PhantomReference | 依賴 JVM 適應性 GC (G1/ZGC/Shenandoah),**極少需手動觸發** |
public class NativeResource implements AutoCloseable {
private static final Cleaner CLEANER = Cleaner.create();
private final long nativeHandle; // JNI 分配的原生記憶體位址
private final Cleaner.Cleanable cleanable;
public NativeResource(long size) {
this.nativeHandle = allocateNative(size); // JNI 呼叫
this.cleanable = CLEANER.register(this, () -> freeNative(nativeHandle));
}
@Override
public void close() { cleanable.clean(); } // 顯式釋放
// 物件不可達時,Cleaner 會在背景執行緒執行清理動作 (非 finalize 執行緒)
private static native long allocateNative(long size);
private static native void freeNative(long handle);
}
💡 鐵律:**永遠不要依賴
finalize()釋放資源**。實作AutoCloseable、配合try-with-resources確定性釋放;長生命週期物件用Cleaner作為安全網。System.gc()僅用於效能測試基準建立,**生產代碼嚴禁呼叫**。
System.in.read() 回傳 **單一位元組值 (0-255 或 -1 表示 EOF)**,不是 整數字面值。輸入 2 得到 ASCII 碼 50 ('2' = 0x32 = 50)。
import java.util.Scanner;
public class InputDemo {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("請輸入整數: ");
if (scanner.hasNextInt()) {
int num = scanner.nextInt();
System.out.println("您輸入: " + num + ",兩倍: " + (num * 2));
} else {
System.out.println("輸入非整數: " + scanner.next());
}
scanner.close();
}
}
public class FastScanner {
private final byte[] buf = new byte[1 << 16];
private int idx = 0, size = 0;
private final InputStream in = System.in;
private byte read() throws IOException {
if (idx >= size) {
size = in.read(buf);
idx = 0;
if (size <= 0) return -1;
}
return buf[idx++];
}
public int nextInt() throws IOException {
byte c; int sign = 1, val = 0;
do c = read(); while (c <= ' '); // 跳過空白
if (c == '-') { sign = -1; c = read(); }
for (; c > ' '; c = read()) val = val * 10 + (c - '0');
return val * sign;
}
}
java.time — 現代日期時間 API 核心套件static nested class — 無外部實例引用的巢狀類別NumberFormatException — 數字解析失敗例外Cleaner — JDK 9+ 替代 finalize 的資源清理機制Scanner / FastScanner — 輸入處理兩大流派