日期時間處理是業務開發的高頻需求。本文系統梳理 JDK 1.0 時代的 java.util.Date、Calendar、GregorianCalendar 與根類別 Object 核心方法,並指出其設計缺陷,為後續遷移至 Java 8+ java.time 奠基。
Date() // 當前毫秒時間戳
Date(long millisSinceEpoch) // 1970-01-01 00:00:00 UTC 起算毫秒數
| 方法 | 說明 | 替代方案 |
|---|---|---|
getTime() | 取得毫秒時間戳 | 繼續使用 |
setTime(long) | 設定毫秒時間戳 | 繼續使用 |
getYear()/setYear() | 年份(1900 起算) | Calendar.YEAR 或 LocalDate.getYear() |
getMonth()/setMonth() | 月份(0-11) | Calendar.MONTH 或 LocalDate.getMonthValue() |
getDate()/setDate() | 日期(1-31) | Calendar.DATE 或 LocalDate.getDayOfMonth() |
getDay() | 星期(0-6) | Calendar.DAY_OF_WEEK |
getHours()/getMinutes()/getSeconds() | 時/分/秒 | Calendar.HOUR_OF_DAY 等 |
toString() | 預設格式:dow mon dd hh:mm:ss zzz yyyy | DateTimeFormatter |
Date now = new Date();
// 線程不安全!區域變數使用或 ThreadLocal 複用
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String formatted = sdf.format(now);
System.out.println(formatted); // 2024-03-15 14:30:45
警示:SimpleDateFormat 非執行緒安全,併發環境必須每次新建或用 ThreadLocal 包裝。
Calendar cal = Calendar.getInstance(); // 實際回傳 GregorianCalendar
cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"), Locale.US); // 指定時區/語系
| 操作 | 方法範例 |
|---|---|
| 設定日期時間 | set(2024, Calendar.MARCH, 15, 14, 30, 45) 注意:月份 0-based(0=1月) |
| 取得欄位值 | int year = cal.get(Calendar.YEAR); |
| 加減時間 | cal.add(Calendar.DAY_OF_MONTH, 7); 一週後cal.roll(Calendar.MONTH, 1); 月份+1,不進位年份 |
| 設定/取得毫秒時間戳 | cal.setTimeInMillis(ms); / cal.getTimeInMillis() |
| 轉 Date | Date d = cal.getTime(); |
| 比較 | cal.before(otherCal), cal.after(otherCal), cal.compareTo(otherCal) |
YEAR, MONTH (0-11), DATE/DAY_OF_MONTH (1-31),
HOUR (0-11), HOUR_OF_DAY (0-23), MINUTE, SECOND, MILLISECOND,
DAY_OF_WEEK (1=Sun...7=Sat), DAY_OF_YEAR, WEEK_OF_YEAR, AM_PM
GregorianCalendar gc = new GregorianCalendar();
boolean leap = gc.isLeapYear(2024); // true
java.lang.Object 是所有類別的隱性父類,定義了通用協定:
| 方法 | 預設行為 | 覆寫契約與最佳實踐 |
|---|---|---|
toString() | ClassName@hashCode | 必覆寫:返回物件有意義的字串表示,便於除錯/日誌。 範例: "User{id=1, name='Alice'}" |
equals(Object) | 比較記憶體位址(==) | 覆寫時必同時覆寫 hashCode()。 契約:自反性、對稱性、傳遞性、一致性、非空性。 常用 Objects.equals(a,b) 處理 null。 |
hashCode() | 記憶體位址衍生值 | 同樣物件必回傳相同值;equals 相等則 hashCode 必相等。常用 Objects.hash(fields...) 生成。 |
clone() | 淺拷貝(需實作 Cloneable) | 建議改用複製建構子或序列化拷貝,避免 Cloneable 破壞封裝。 |
finalize() | GC 前調用(已棄用 Java 9) | 不要使用:執行時間不確定、影響 GC 效能、可能復活物件。 資源清理用 try-with-resources / Cleaner / PhantomReference。 |
getClass() | 回傳 Class 物件 | final,不可覆寫。反射入口。 |
wait/notify/notifyAll | 監視器等待/通知 | 需在 synchronized 區塊內呼叫。現代併發優先用 Condition / LockSupport / CompletableFuture。 |
Date/Calendar 可變 → 併發 Bug 溫床。java.time 全不可變(LocalDateTime, ZonedDateTime, Instant)。Calendar.JANUARY = 0 違反直覺。Month.JANUARY = 1。Date.getYear() = 124 代表 2024。LocalDate.getYear() = 2024。DateTimeFormatter 不可變、執行緒安全。ZoneId, ZonedDateTime 一流支援。理解 Date/Calendar 歷史包袱,能幫你維護舊代碼並平滑遷移。新專案**直接使用 java.time 套件**,享受不可變、流暢 API、標準 ISO-8601 支援帶來的開發體驗與正確性保障。