杰瑞科技汇

Java String与Byte互转有哪些注意事项?

  • String -> byte[]: 将字符串中的字符按照指定的字符集编码成字节序列。
  • byte[] -> String: 将一串字节序列按照指定的字符集解码成字符,并组合成字符串。

核心要点

  1. 字符集是关键:你必须始终明确指定字符集(如 UTF-8, GBK, ISO-8859-1),如果编码和解码使用的字符集不一致,就会出现乱码。
  2. 首选 UTF-8:在绝大多数现代应用中,UTF-8 是首选的字符集,它能支持全球几乎所有的语言,并且是互联网上的标准。
  3. 避免使用平台默认字符集:不要使用 String.getBytes()new String(byte[]) 这种不带字符集参数的方法,因为它们会使用 JVM 运行时所在操作系统的默认字符集(如 Windows 可能是 GBK,Linux/macOS 可能是 UTF-8),这会导致代码在不同环境下产生不一致的结果,是乱码的主要来源。

String 转 byte[] (编码)

使用 String 类的 getBytes() 方法。

Java String与Byte互转有哪些注意事项?-图1
(图片来源网络,侵删)

推荐方式:明确指定字符集

这是最安全、最推荐的做法。

import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
public class StringToBytes {
    public static void main(String[] args) {
        String str = "你好,Java! Hello, World!";
        // --- 推荐方式:使用 StandardCharsets (Java 7+) ---
        // StandardCharsets 是一个枚举类,提供了标准的字符集常量,比字符串更安全,不会有拼写错误。
        try {
            byte[] utf8Bytes = str.getBytes(StandardCharsets.UTF_8);
            System.out.println("UTF-8 字节数组: " + java.util.Arrays.toString(utf8Bytes));
            byte[] gbkBytes = str.getBytes("GBK"); // 也可以直接传入字符集名称字符串
            System.out.println("GBK 字节数组: " + java.util.Arrays.toString(gbkBytes));
        } catch (UnsupportedEncodingException e) {
            // 对于 StandardCharsets.UTF_8 这种标准字符集,这个异常理论上不会发生
            // 但对于传入字符串名称的方式,如果字符集不支持,则会抛出此异常
            e.printStackTrace();
        }
        // --- 不推荐的方式:使用平台默认字符集 ---
        // 代码在不同操作系统上运行结果可能不同,可能导致问题。
        byte[] defaultBytes = str.getBytes();
        System.out.println("默认字符集字节数组: " + java.util.Arrays.toString(defaultBytes));
    }
}

输出分析: 你会看到 UTF-8GBK 编码后的字节数组是完全不同的,这就是为什么必须指定字符集的原因。


byte[] 转 String (解码)

使用 String 的构造函数。

推荐方式:明确指定字符集

import java.nio.charset.StandardCharsets;
public class BytesToString {
    public static void main(String[] args) {
        // 假设这是从网络或文件中读取的字节数据,它原本是用 UTF-8 编码的
        byte[] utf8Bytes = {-28, -72, -83, -26, -106, -121, 44, 74, 97, 118, 97, 33, 32, 72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33};
        // --- 推荐方式:使用与编码时相同的字符集 ---
        String strFromUtf8 = new String(utf8Bytes, StandardCharsets.UTF_8);
        System.out.println("从 UTF-8 字节数组解码: " + strFromUtf8);
        // --- 错误示范:使用了错误的字符集 ---
        // 如果我们错误地用 GBK 去解码一个 UTF-8 编码的字节数组,就会出现乱码
        String strFromGbk = new String(utf8Bytes, "GBK");
        System.out.println("错误地用 GBK 解码 UTF-8 字节 (乱码): " + strFromGbk);
        // --- 另一个错误示范:使用平台默认字符集 ---
        // 同样,不推荐使用
        String strFromDefault = new String(utf8Bytes);
        System.out.println("用默认字符集解码: " + strFromDefault);
    }
}

输出分析:

Java String与Byte互转有哪些注意事项?-图2
(图片来源网络,侵删)
  • 从 UTF-8 字节数组解码: 正确地输出了 "你好,Java! Hello, World!"。
  • 错误地用 GBK 解码 UTF-8 字节 (乱码): 会输出一堆看不懂的字符,这就是乱码。
  • 用默认字符集解码: 结果取决于你当前运行代码的系统的默认编码,可能是对的,也可能是错的。

常见问题与最佳实践

问题1:处理不完整的字节序列

byte[] 不是一个完整的字符序列(截断了多字节字符的某一部分),用 new String() 解码时会抛出 MalformedInputException 或用替换字符()代替。

import java.nio.charset.StandardCharsets;
import java.nio.charset.MalformedInputException;
public class IncompleteByteSequence {
    public static void main(String[] args) {
        // "中" 字的 UTF-8 编码是 3 个字节: [-28, -72, -83]
        byte[] incompleteBytes = {-28, -72}; // 只取了前两个字节
        try {
            // 这会抛出异常,因为这是一个不完整的 UTF-8 序列
            String str = new String(incompleteBytes, StandardCharsets.UTF_8);
            System.out.println(str);
        } catch (MalformedInputException e) {
            System.err.println("错误:输入的字节序列不完整或格式错误!");
        }
    }
}

最佳实践:如何正确处理不完整数据?

在网络传输中,数据分片接收是很常见的,你需要一个策略来处理这种情况。

  1. 丢弃不完整的数据:最简单粗暴,直接忽略最后一个不完整的字符。
  2. 使用 CharsetDecoder:这是最灵活、最强大的方式。CharsetDecoder 可以让你配置如何处理错误,例如用替换符代替错误序列。
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.MalformedInputException;
public class SafeDecoder {
    public static void main(String[] args) throws CharacterCodingException {
        byte[] incompleteBytes = {-28, -72}; // "中" 字不完整的 UTF-8 编码
        // --- 方式一:严格模式(默认)---
        try {
            String strictStr = new String(incompleteBytes, StandardCharsets.UTF_8);
            System.out.println("严格模式解码: " + strictStr);
        } catch (MalformedInputException e) {
            System.out.println("严格模式解码失败: " + e.getMessage());
        }
        // --- 方式二:宽松模式(用替换符 � 代替错误序列)---
        CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder()
                .onMalformedInput(CodingErrorAction.REPLACE) // 遇到不完整序列时替换
                .onUnmappableCharacter(CodingErrorAction.REPLACE); // 遇到无法映射的字符时替换
        try {
            CharBuffer charBuffer = decoder.decode(ByteBuffer.wrap(incompleteBytes));
            String lenientStr = charBuffer.toString();
            System.out.println("宽松模式解码: " + lenientStr); // 输出 "�"
        } catch (CharacterCodingException e) {
            e.printStackTrace();
        }
    }
}

总结表格

操作 推荐方法 说明
String -> byte[] str.getBytes(StandardCharsets.UTF_8) 必须指定字符集。StandardCharsets 是 Java 7+ 的安全选择。
byte[] -> String new String(byte[], StandardCharsets.UTF_8) 必须使用与编码时相同的字符集,否则会乱码。
处理不完整字节 使用 CharsetDecoder 并设置 CodingErrorAction 在网络或流式数据处理中非常有用,可以更优雅地处理错误。
避免 str.getBytes()new String(byte[]) 依赖平台默认字符集,是导致跨平台乱码的罪魁祸首,应绝对避免。

记住这个黄金法则:编码和解码必须使用同一种字符集,并且永远不要依赖默认值

Java String与Byte互转有哪些注意事项?-图3
(图片来源网络,侵删)
分享:
扫描分享到社交APP
上一篇
下一篇