首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Java 入門 FAQ 精選 (三):日期格式化、巢狀類別、輸入判斷、GC 機制

     編輯:Java 程式設計入門

FAQ 9:日期時間格式化最佳實務

舊 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);
    }
}

舊 API 遷移對照表

舊類別現代替代關鍵差異
java.util.DateInstant (時間戳) / ZonedDateTime (帶時區)不可變、奈秒精度、時區明確
java.util.CalendarZonedDateTime / LocalDateTime流暢 API、不可變、無偏移月份陷阱
SimpleDateFormatDateTimeFormatter執行緒安全、可快取重用、支援區域化
TimeZoneZoneId / ZoneOffsetIANA 時區資料庫、歷史變更支援

⚠️ 嚴禁 在多執行緒環境共用 SimpleDateFormat 實例。若必須維護舊代碼,使用 ThreadLocal<SimpleDateFormat> 或每次 new SimpleDateFormat(...)。


FAQ 10:靜態 vs 非靜態巢狀類別核心差異

特性靜態巢狀類別 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(); }
}

FAQ 11:判斷字串是否為數字的多種方案

方法適用型別效能特點範例
Integer.parseInt(s) / Long.parseLong(s) / Double.parseDouble(s)整數/長整數/浮點數高 (原生)**標準做法**,非數字拋 NumberFormatException,需 try-catchtry { 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);
    }
}

FAQ 12:finalize() vs System.gc() 深度解析

維度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),**極少需手動觸發**

Cleaner 範例 (JDK 9+ 推薦替代 finalize)

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() 僅用於效能測試基準建立,**生產代碼嚴禁呼叫**。


FAQ 13:System.in.read() 陷阱與正確輸入處理

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 — 輸入處理兩大流派
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved