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

Java char 陣列與 String 互轉完整指南:核心方法、底層原理、效能分析與最佳實踐

     編輯:Java 相關知識

前言

在 Java 開發中,`char[]` (字符陣列) 與 `String` (字符串) 的相互轉換是高頻操作:密碼框 `JPasswordField.getPassword()` 返回 `char[]`、加密算法處理字符陣列、字符串構建器底層即字符陣列、與 Native/C++ 交互等。本文系統梳理**核心轉換方法、底層實現原理、效能差異、安全性考量、最佳實踐**,助你寫出高效、安全的代碼。

一、核心轉換方法速查表

方向方法說明備註
String → char[]str.toCharArray()**標準方法**,返回**新陣列**副本,修改不影響原字符串最常用、線程安全
String → char[]str.getChars(srcBegin, srcEnd, dst, dstBegin)將指定範圍複製到**目標陣列**,避免創建新陣列高性能、批量處理
char[] → Stringnew String(char[])**標準構造器**,複製陣列內容最常用
char[] → StringString.valueOf(char[])靜態工廠方法,內部調用 `new String(char[])`,`null` 安全返回 `"null"`推薦、語義清晰
char[] → Stringnew String(char[], offset, count)構造子範圍字符串零拷貝 (JDK 9+ Compact Strings)
char[] → StringString.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'); // 使用後立即清零,減少內存洩漏風險
    }
}

三、底層原理深度解析 (JDK 9+ Compact Strings)

String 內部存儲演進

  • JDK 8 及之前:`private final char[] value;` 佔用 2 bytes/char (UTF-16)。
  • JDK 9+ (JEP 254 Compact Strings):`private final byte[] value;` + `private final byte coder;` (0=LATIN1/1 byte, 1=UTF16/2 bytes)。**純 ASCII 字符串內存減半**。

轉換時的拷貝行為

  1. toCharArray():總是創建新 `char[]` 並拷貝數據。JDK 9+ 還需將 `byte[]` 解碼為 `char[]` (LATIN1 直接擴展,UTF16 直接拷貝)。
  2. new String(char[]) / `valueOf()`:總是創建新 `byte[]` 並拷貝數據。JDK 9+ 會嘗試壓縮為 LATIN1 (若所有 char < 256)。
  3. getChars():用戶提供目標 `char[]`,**無新陣列分配**,僅拷貝數據,適合高頻、大數據場景。

四、效能微基準測試參考 (JMH)


@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[] 而非 String?

  1. 不可變性風險:`String` 不可變,修改不了內容,**無法主動清零**。密碼在堆中存活直到 GC,甚至可能進入 Heap Dump、Swap 文件、Core Dump,被攻擊者讀取。
  2. 字符串常量池:字面量或 `intern()` 會進入常量池,**永久駐留**,更難清除。
  3. char[] 可變清零:`Arrays.fill(password, '0')` 可立即覆蓋內存,**縮短敏感數據暴露窗口**。Java 官方建議 (如 `JPasswordField`、`Console.readPassword()`) 均返回 `char[]`。

最佳實踐:接收密碼 `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, "")` 返回空串

七、進階:StringBuilder / StringBuffer 底層即字符陣列

  • `StringBuilder` 內部維護 `byte[] value` (JDK 9+) / `char[] value` (JDK 8),`append` 擴容策略 (1.5倍/2倍)。
  • `toString()` 在 JDK 9+ **不再拷貝**,直接共享 `byte[]` (String 也是 `byte[]`),**零拷貝構造字符串**!這是 `StringBuilder` 高效的關鍵。
  • 因此:**大量拼接請用 `StringBuilder`,最後 `sb.toString()` 一次性生成 String,極致高效。

八、結語

`char[]` 與 `String` 互轉看似簡單,實則涉及**內存分配策略、Compact Strings 編碼轉換、安全性清零、零拷貝優化**等深層機制。掌握 `toCharArray()`/`valueOf()` 標準用法、善用 `getChars()` 避免分配、理解 `StringBuilder` 底層共享機制、嚴格執行密碼 `char[]` 清零規範,是寫出高性能、高安全性 Java 代碼的關鍵細節功夫。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved