byte[] bytes = str.getBytes(); —— 這看似無害的代碼,是無數 Java 專案在從 Windows 遷移到 Linux、從國內環境部署到海外服務器時出現亂碼、數據長度不一致的元兇。本文深入剖析 getBytes() 的平台相關性本質,演示實際差異,並給出生產級編碼處理規範。
String s = "Hello!你好!";
byte[] bytes = s.getBytes();
System.out.println(bytes.length);
| 運行環境 | 輸出長度 | 默認編碼 | 中文編碼方式 |
|---|---|---|---|
| 中文 Windows XP | 12 | GBK/GB2312 | 雙字節 |
| 英文 UNIX/Linux | 9 | ISO-8859-1 | 單字節(非拉丁字符變 ?) |
根本原因:String.getBytes() 無參重載使用 Charset.defaultCharset(),而默認字符集由 file.encoding 系統屬性決定,隨操作系統、區域設置、JVM 啟動參數變化。
無論平台如何,Java 字符串在內存中始終以 UTF-16 (Unicode) 存儲:
char c = '你'; // Unicode: \u4F60
int codePoint = c; // 20320 (0x4F60)
編譯時 javac -encoding GBK 只影響源文件讀取,.class 文件中字符串常量池仍存儲 Unicode。編碼轉換發生在 字符串 → 字節數組 的獲取過程中。
// 寫死 UTF-8,跨平台一致,支持全字符集
byte[] bytes = str.getBytes(StandardCharsets.UTF_8); // Java 7+
// 或
byte[] bytes = str.getBytes("UTF-8"); // 兼容舊版本,需捕獲 UnsupportedEncodingException
String s = "Hello!你好!";
byte[] isoBytes = s.getBytes("ISO-8859-1"); // [72,101,108,108,111,33,63,63,63] 中文變 63(?)
byte[] gbkBytes = s.getBytes("GBK"); // [72,101,108,108,111,33,-60,-29,-70,-61,-93,-95]
byte[] utf8Bytes = s.getBytes("UTF-8"); // [72,101,108,108,111,33,-28,-67,-96,-27,-128,-113,-28,-67,-96,-27,-128,-113]
0x3F (?),數據不可逆損壞String restored = new String(bytes, StandardCharsets.UTF_8); // 必須與編碼時一致
鐵律:編碼與解碼必須使用完全相同的字符集,否則產生亂碼。
-Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8.properties 文件需為 UTF-8,或使用 ResourceBundle 配合 Control 自定義編碼utf8mb4,連接串含 characterEncoding=utf8// 讀取 GBK 編碼的舊文件
String content = Files.readString(Paths.get("legacy.txt"), StandardCharsets.ISO_8859_1); // 先按單字節讀取
byte[] gbkBytes = content.getBytes(StandardCharsets.ISO_8859_1); // 還原原始字節
String correct = new String(gbkBytes, "GBK"); // 按真實編碼解碼
String.getBytes() 的平台相關性是 Java 跨平台承諾中的「例外」。永遠顯式指定字符集,統一採用 UTF-8,是消除編碼亂碼的唯一正解。將編碼處理視為系統邊界的契約,而非內部實現細節,才能構建真正可移植的 Java 應用。