本文匯總 Java 核心高級特性中的高頻面試考點與實戰易錯點,涵蓋集合框架差異、隨機數陷阱、字符串拼接行為、線程安全類特征與字符長度計算,助力面試突擊與代碼質量提升。
1. HashMap vs Hashtable 核心差異
| 維度 | HashMap | Hashtable |
| 線程安全 | 非線程安全 | 同步方法 (synchronized),線程安全 |
| Null 鍵/值 | 允許 1 個 null key、多個 null value | 不允許 null key/value,拋 NPE |
| 繼承體系 | AbstractMap | Dictionary (舊 API) |
| 迭代器 | fail-fast (快速失敗) | Enumerator (非 fail-fast) |
| 性能 | 高 (無同步開銷) | 低 (全鎖同步) |
| 初始容量/擴容 | 16 → 2倍 | 11 → 2n+1 |
| JDK 1.8 優化 | 鏈表轉紅黑樹 (閾值 8) | 無 |
生產環境選型建議
- 單線程/非併發:
HashMap
- 多線程高併發讀寫:
ConcurrentHashMap (JDK 1.8 CAS + synchronized + 紅黑樹)
- 舊代碼維護/強一致性需求:
Collections.synchronizedMap(new HashMap()) 或 Hashtable
2. Random.nextInt(n) 邊界陷阱
// 常見錯誤認知
Random r = new Random();
int num = r.nextInt(10) + 1; // 實際範圍:[1, 10]
// nextInt(10) 返回 [0, 9] → +1 後 [1, 10]
// 正確理解
// nextInt(bound) 返回 [0, bound-1]
// 如需 [min, max] 閉區間:
int randomInRange = r.nextInt(max - min + 1) + min;
ThreadLocalRandom (JDK 7+ 推薦)
// 多線程環境下性能優於 Random
int num = ThreadLocalRandom.current().nextInt(1, 11); // [1, 10] 左閉右開
3. 字符串 + 操作符重載行為
編譯期優化:字面量常量折疊
String s1 = "a" + "b" + "c"; // 編譯期直接優化為 "abc"
String s2 = "a" + 1 + 2; // "a12" (左到右:("a"+1)="a1" → "a1"+2="a12")
String s3 = 1 + 2 + "a"; // "3a" (左到右:(1+2)=3 → 3+"a"="3a")
運行期:StringBuilder 自動優化
String a = "a";
String b = "b";
String s = a + b; // 反編譯等價:
// new StringBuilder().append(a).append(b).toString()
循環中拼接性能殺手
// 反模式:每次循環創建 StringBuilder
String result = "";
for (int i = 0; i < 10000; i++) {
result += i; // 極慢,O(n²)
}
// 正確:複用 StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sb.append(i);
}
String result = sb.toString();
4. 線程安全類特征與選型
JDK 內建線程安全類 (舊 API)
Vector - 同步方法的動態數組 (推薦 ArrayList + 外部同步/並發包)
Hashtable - 同步方法的哈希表 (推薦 ConcurrentHashMap)
Stack - 繼承 Vector (推薦 ArrayDeque)
Enumeration - 疊代器 (推薦 Iterator)
StringBuffer - 同步字符串構建器 (推薦 StringBuilder 單線程)
現代並發包替代方案 (java.util.concurrent)
| 舊類 | 現代替代 | 優勢 |
| Vector | CopyOnWriteArrayList / List + ReentrantLock | 讀多寫少 / 精細控制 |
| Hashtable | ConcurrentHashMap | 分段鎖/CAS,高併發吞吐 |
| Stack | ArrayDeque / ConcurrentLinkedDeque | 非同步雙端隊列 / 線程安全棧 |
| StringBuffer | StringBuilder (單線程) / StringJoiner | 無鎖開銷 / 格式化拼接 |
核心原則
線程安全類方法級同步 ≠ 復合操作線程安全
例:if (!hashtable.containsKey(key)) hashtable.put(key, value); 仍需外部同步
5. 字符長度計算規則
// Java char 為 UTF-16 代碼單元 (2 字節)
String s = "a中😀"; // 英文、中文、Emoji
s.length(); // 4 (UTF-16 代碼單元數:a=1, 中=1, 😀=2 個 surrogate pair)
s.codePointCount(0, s.length()); // 3 (實際 Unicode 碼點數)
s.getBytes(StandardCharsets.UTF_8).length; // 7 (UTF-8 字節數:a=1, 中=3, 😀=4)
常見長度單位對照
| 字符類型 | char/length() | codePointCount() | UTF-8 bytes | UTF-16 bytes |
| ASCII (a-Z, 0-9, 符號) | 1 | 1 | 1 | 2 |
| 中日韓統一表意文字 (CJK) | 1 | 1 | 3 | 2 |
| Emoji / 輔助平面字符 | 2 (surrogate pair) | 1 | 4 | 4 |
6. 綜合實戰題:找出代碼問題
// 題目 1:Map 併發
Map<String, Integer> map = new HashMap<>();
// 多線程 put/get → 可能導致死循環 (JDK 7 擴容鏈表成環) 或數據丟失
// 修正:ConcurrentHashMap
// 題目 2:隨機數
int prize = new Random().nextInt(100) + 1; // [1, 100] ✓
// 但若作為安全令牌 → 不安全,用 SecureRandom
// 題目 3:字符串拼接
String sql = "SELECT * FROM user WHERE id = " + id + " AND name = '" + name + "'";
// 風險:SQL 注入 + 拼接性能
// 修正:PreparedStatement / StringBuilder
// 題目 4:線程安全復合操作
Vector<String> v = new Vector<>();
if (v.isEmpty()) v.add("first"); // 非原子!兩線程同時進入 → 重複添加
// 修正:synchronized(v) { if (v.isEmpty()) v.add("first"); }
// 題目 5:字符長度截取
String sub = s.substring(0, 10); // 可能截斷 Emoji surrogate pair → 亂碼
// 修正:按 codePoint 截取或用 StringBuilder + codePoint 判斷
總結
Java 高級特性考點往往隱藏在「看似簡單的 API 行為差異」中:HashMap 非線程安全與擴容機制、Random 邊界與安全性、字符串拼編譯期/運行期雙重語義、線程安全類「方法安全 ≠ 復合安全」、字符長度的多重度量標準。掌握本質、避開陷阱、選對工具,是從「會用」到「精通」的關鍵。