Java 生態系統持續演進,JDK 標準庫在某些領域設計滯後。本文介紹 6 個生產級專案幾乎必用的現代類庫,它們解決了 JDK 的痛點,大幅提升開發效率與代碼質量。無論你是維護舊專案還是啟動新專案,這些類庫都值得納入技術棧。
JDK 集合、字串、快取、並發等 API 設計不夠人性化。Guava 是 JDK 的「強力補完計畫」,甚至有人說「應該併入 JDK」。
// Maven 依賴
// com.google.guava:guava:31.1-jre
// 統計單詞出現次數 - 一行代碼搞定
String content = Files.asCharSource(new File(args[0]), StandardCharsets.UTF_8).read();
Multiset<String> wordCount = HashMultiset.create(
Splitter.on(CharMatcher.WHITESPACE)
.omitEmptyStrings()
.trimResults()
.split(content)
);
// 輸出: [hello×3, world×2, java×1]
System.out.println(wordCount);
| 模組 | 核心功能 | 典型用途 |
|---|---|---|
| Collections | Multiset, Multimap, Table, Immutable 集合, Ordering | 替代 JDK 集合,不可變集合防禦性編程 |
| Caching | LoadingCache, 多種過期策略 (時間、大小、引用) | 本地快取,替代手寫 Map + 定時清理 |
| Primitives | 基本類型工具,無符號支援 | 避免自動裝箱開銷 |
| Concurrency | ListenableFuture, RateLimiter, Service 框架 | 簡化非同步編程,限流器 |
| Strings | Joiner, Splitter, CharMatcher, CaseFormat | 字串分割、連接、大小寫轉換 |
| EventBus | 發布-訂閱,無需顯式註冊 | 模組解耦,替代觀察者模式手寫 |
| I/O | Files, Resources, ByteStreams, CharStreams | 簡化檔案/資源讀寫 (Java 7+ NIO.2 部分替代) |
| Hash | Hashing, BloomFilter, ConsistentHash | 分散式系統、快取穿透防護 |
SimpleDateFormat 非線程安全// Maven: joda-time:joda-time:2.12.5
// 建立日期 - 參數語義清晰
DateTime firstDay2013 = new DateTime().withDate(2013, 1, 1); // 2013-01-01T00:00:00.000
// 日期運算 - 流暢 API
DateTime twoDaysLater = firstDay2013.plusDays(2); // 2013-01-03
DateTime lastMonth = firstDay2013.minusMonths(1); // 2012-12-01
boolean isLeap = firstDay2013.dayOfYear().withMaximumValue().getDayOfYear() == 366;
// 格式化 - 線程安全,可重用
DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
String formatted = fmt.print(firstDay2013); // "2013-01-01 00:00:00"
DateTime parsed = fmt.parseDateTime("2013-12-25 12:00:00");
// 區間與持續時間
Interval interval = new Interval(start, end);
Period period = interval.toPeriod(); // 年月日時分秒
Duration duration = interval.toDuration(); // 標準毫秒數
Java 8+ 專案 → 直接使用 java.time.* (JSR-310),API 與 Joda-Time 高度相似。Java 7 或舊專案 → 必須用 Joda-Time。
// 誰是預期?誰是實際?記不住! assertEquals(expected, actual); // JUnit 3 風格 assertEquals(actual, expected); // JUnit 4+ 風格,易搞混
18 && u.isActive();
}
public void describeTo(Description d) { d.appendText("成年活躍用戶"); }
});
JUnit 4.4+ 內建 Hamcrest 核心,無需額外依賴。JUnit 5 推薦使用 org.hamcrest:hamcrest:2.2 完整版。
| 框架 | 痛點 | Mockito 優勢 |
|---|---|---|
| JMock | 需寫 checking block,語法繁瑣 | 無 checking,直觀 API |
| EasyMock | 需 replay/verify 切換模式 | 無模式切換,行為驗證更自然 |
// Maven: org.mockito:mockito-core:5.3.1
// 1. 建立 Mock
List<String> mockedList = mock(List.class);
// 2. 存根行為
when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(1)).thenThrow(new RuntimeException("error"));
when(mockedList.size()).thenReturn(10);
// 3. 驗證調用
verify(mockedList).get(0);
verify(mockedList, times(2)).get(anyInt());
verify(mockedList, never()).clear();
// 4. 參數捕獲
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
verify(mockedList).add(captor.capture());
assertEquals("captured", captor.getValue());
// 5. Spy (部分 Mock 真實物件)
List<String> spyList = spy(new ArrayList<>());
when(spyList.size()).thenReturn(100); // 覆蓋特定方法
spyList.add("real"); // 真實調用
// Maven 依賴
// org.slf4j:slf4j-api:2.0.7
// ch.qos.logback:logback-classic:1.4.7
// 代碼中只依賴 SLF4J API
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Service {
private static final Logger log = LoggerFactory.getLogger(Service.class);
public void process(String name) {
// 佔位符語法:延遲字串構建,效能極佳
log.debug("處理用戶: {}", name);
log.info("訂單 {} 金額: {}", orderId, amount);
log.error("處理失敗", exception); // 異常自動打印堆棧
}
}
<configuration>
<!-- 控制台輸出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- 檔案輸出,按日期滾動 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/app.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
</configuration>
| Log4j (舊) | Logback (新) | 含義 |
|---|---|---|
| %d{dd MMM yyyy HH:mm:ss} | %d{yyyy-MM-dd HH:mm:ss.SSS} | 時間戳 |
| [%t] | [%thread] | 線程名 |
| %-5p | %-5level | 日誌級別 |
| %m | %msg | 訊息內容 |
| %n | %n | 換行 |
Logback 模式語義自明,無需查文檔。
| 場景 | 推薦方案 | 替代方案 |
|---|---|---|
| 集合增強、快取、字串處理 | Guava | Apache Commons, Eclipse Collections |
| 日期時間 (Java 7/舊專案) | Joda-Time | java.time (Java 8+) |
| 單元測試斷言 | Hamcrest (JUnit 內建) | AssertJ (更流暢), Truth |
| Mock 測試雙工 | Mockito | JMockit (強大但複雜), EasyMock |
| 日誌門面 + 實現 | SLF4J + Logback | Log4j 2.x (效能近似), java.util.logging |
這 6 個類庫解決了 JDK 核心缺口:
將它們納入專案標準化模板,能讓團隊專注業務邏輯,而非重複造輪子或與糟糕 API 搏鬥。技術選型無絕對標準,但「不造輪子、用成熟方案」永遠是工程智慧的體現。