在 Java 開發中,`char[]` (字符陣列) 與 `String` (字符串) 的相互轉換是高頻操作:密碼框 `JPasswordField.getPassword()` 返回 `char[]`、加密算法處理字符陣列、字符串構建器底層即字符陣列、與 Native/C++ 交互等。本文系統梳理**核心轉換方法、底層實現原理、效能差異、安全性考量、最佳實踐**,助你寫出高效、安全的代碼。
| 方向 | 方法 | 說明 | 備註 |
|---|---|---|---|
| String → char[] | str.toCharArray() | **標準方法**,返回**新陣列**副本,修改不影響原字符串 | 最常用、線程安全 |
| String → char[] | str.getChars(srcBegin, srcEnd, dst, dstBegin) | 將指定範圍複製到**目標陣列**,避免創建新陣列 | 高性能、批量處理 |
| char[] → String | new String(char[]) | **標準構造器**,複製陣列內容 | 最常用 |
| char[] → String | String.valueOf(char[]) | 靜態工廠方法,內部調用 `new String(char[])`,`null` 安全返回 `"null"` | 推薦、語義清晰 |
| char[] → String | new String(char[], offset, count) | 構造子範圍字符串 | 零拷貝 (JDK 9+ Compact Strings) |
| char[] → String | String.copyValueOf(char[]) | 等同 `valueOf`,歷史兼容方法 | 不推薦新代碼使用 |
public class CharArrayStringConversion {
public static void main(String[] args) {
String str = "Hello Java";
// 1. String -> char[] (標準)
char[] arr1 = str.toCharArray();
System.out.println(Arrays.toString(arr1)); // [H, e, l, l, o, , J, a, v, a]
// 2. String -> char[] (指定範圍複製到現有陣列)
char[] dest = new char[5];
str.getChars(0, 5, dest, 0); // 複製 "Hello"
System.out.println(new String(dest)); // Hello
// 3. char[] -> String (構造器)
String s1 = new String(arr1);
// 4. char[] -> String (valueOf 推薦)
String s2 = String.valueOf(arr1);
// 5. 子範圍構造
String sub = new String(arr1, 0, 5); // "Hello"
// 6. 密碼框實戰:char[] 清零
char[] password = "secret123".toCharArray();
// ... 使用 password 傳給加密 API ...
Arrays.fill(password, '0'); // 使用後立即清零,減少內存洩漏風險
}
}
toCharArray():總是創建新 `char[]` 並拷貝數據。JDK 9+ 還需將 `byte[]` 解碼為 `char[]` (LATIN1 直接擴展,UTF16 直接拷貝)。new String(char[]) / `valueOf()`:總是創建新 `byte[]` 並拷貝數據。JDK 9+ 會嘗試壓縮為 LATIN1 (若所有 char < 256)。getChars():用戶提供目標 `char[]`,**無新陣列分配**,僅拷貝數據,適合高頻、大數據場景。
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class ConversionBenchmark {
private static final String TEST_STR = "A".repeat(1000); // 1000 字符
private static final char[] TEST_ARR = TEST_STR.toCharArray();
@Benchmark
public char[] toCharArray() { return TEST_STR.toCharArray(); }
@Benchmark
public void getChars() {
char[] dest = new char[TEST_STR.length()];
TEST_STR.getChars(0, TEST_STR.length(), dest, 0);
}
@Benchmark
public String newString() { return new String(TEST_ARR); }
@Benchmark
public String valueOf() { return String.valueOf(TEST_ARR); }
}
/*
典型結果 (JDK 17, 1000 字符):
toCharArray ~ 250 ns/op (含新陣列分配 + 解碼)
getChars ~ 120 ns/op (無新陣列分配,僅拷貝)
newString ~ 300 ns/op (含新 byte[] 分配 + 壓縮檢查 + 拷貝)
valueOf ~ 300 ns/op (同 newString)
*/
結論:`getChars()` 避免分配目標陣列,性能最優;`toCharArray()`/`new String()` 分配新陣列,開銷較大。高頻熱點路徑建議複用緩衝陣列。
最佳實踐:接收密碼 `char[]` → 傳給加密/哈希 API (如 `MessageDigest.update(byte[])` 需轉 byte[]) → **立即 `Arrays.fill(charArr, '0')` 清零** → 讓 GC 回收。
| 場景 | 錯誤做法 | 正確做法 |
|---|---|---|
| 密碼處理 | `String pwd = new String(passwordField.getPassword())` | 全程使用 `char[]`,用畢 `Arrays.fill(pwd, '0')` |
| 大字符串轉 char[] 修改單字符 | `char[] arr = bigStr.toCharArray(); arr[0]='X'; String ns = new String(arr);` | 用 `StringBuilder` 或 `char[]` 直接操作,避免雙向轉換雙重拷貝 |
| 頻繁拼接字符串 | `str += "a"` 循環 | `StringBuilder` / `StringJoiner` (內部 `char[]`/`byte[]` 動態擴容) |
| 子字符串提取 | `str.substring(0, 10).toCharArray()` | `str.getChars(0, 10, dest, 0)` 直接填充目標陣列 |
| null 安全 | `String.valueOf(nullCharArray)` 返回 `"null"` 字符串 | 提前判空或用 `Objects.toString(charArr, "")` 返回空串 |
`char[]` 與 `String` 互轉看似簡單,實則涉及**內存分配策略、Compact Strings 編碼轉換、安全性清零、零拷貝優化**等深層機制。掌握 `toCharArray()`/`valueOf()` 標準用法、善用 `getChars()` 避免分配、理解 `StringBuilder` 底層共享機制、嚴格執行密碼 `char[]` 清零規範,是寫出高性能、高安全性 Java 代碼的關鍵細節功夫。