杰瑞科技汇

java 编码格式 utf 8

什么是 UTF-8?

UTF-8(8-bit Unicode Transformation Format)是一种针对 Unicode 的可变长度字符编码,它具有以下特点:

  1. 兼容 ASCII:UTF-8 完全兼容 ASCII 编码,对于英文字符(0-127),UTF-8 的编码方式与 ASCII 完全相同,每个字符占 1 个字节,这使得旧的 ASCII 系统可以无缝处理 UTF-8 文本。
  2. 可变长度:UTF-8 使用 1 到 4 个字节来表示一个 Unicode 字符。
    • 1 个字节:表示 U+0000 到 U+007F 的字符(基本 ASCII)。
    • 2 个字节:表示 U+0080 到 U+07FF 的字符(包括大部分欧洲语言的字母)。
    • 3 个字节:表示 U+0800 到 U+FFFF 的字符(包括中文、日文、韩文等常用 CJK 字符,以及大部分 Emoji)。
    • 4 个字节:表示 U+10000 到 U+10FFFF 的字符(生僻汉字、历史文字、部分 Emoji 等)。
  3. 无 BOM:UTF-8 通常不需要字节顺序标记,虽然可以有一个 BOM(\uFEFF)来标识文件是 UTF-8 编码,但这在 Java 等现代语言中不推荐,因为它可能会在某些工具中引起问题。

为什么在 Java 中必须使用 UTF-8?

Java 语言内部使用 UTF-16 编码来表示字符串(String 类型),这意味着:

  • 当你在 Java 代码中写一个字符串字面量,如 String str = "你好";,JVM 会将这些字符内部存储为 UTF-16 编码。
  • 当你的 Java 代码源文件(.java)被编译时,Javac 编译器需要读取这些源文件,如果源文件是 UTF-8 编码的,编译器必须正确地将其解码为内部的 UTF-16 字符串。
  • 当你的程序需要与外部世界交互时(读取文件、网络数据、控制台输入输出),就必须将内部的 UTF-16 字符串转换为外部编码(通常是 UTF-8),或者将外部编码的数据解码为内部的 UTF-16 字符串。

如果不统一使用 UTF-8,就会导致“乱码”(Garbled Characters),一个用 GBK 编码保存的中文文件,如果用默认的 ISO-8859-1 编码去读,就会出现看不懂的乱码。


在 Java 中如何确保使用 UTF-8?

你需要从以下几个方面进行设置和编码:

源代码文件(.java)编码

这是最基本的一步,确保你的所有 .java 源文件都保存为 UTF-8 编码。

  • IDE (如 IntelliJ IDEA / Eclipse) 设置

    • IntelliJ IDEA: File -> Settings -> Editor -> File Encodings,将 Global EncodingProject EncodingDefault encoding for properties files 都设置为 UTF-8
    • Eclipse: Window -> Preferences -> General -> Workspace,将 Text file encoding 设置为 UTF-8,在 Preferences -> General -> Content Types 中,确保 Java Source File 的编码也设置为 UTF-8
  • 构建工具 (如 Maven / Gradle)

    • Maven: 在 pom.xml 中,<project> 标签下添加或修改以下配置,这会强制 Maven 使用 UTF-8 来编译源码和读取资源文件。
      <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
      </properties>
    • Gradle: 在 build.gradle 文件中添加以下代码:
      tasks.withType(JavaCompile) {
        options.encoding = 'UTF-8'
      }
      tasks.withType(Test) {
        systemProperty 'file.encoding', 'UTF-8'
      }

运行时环境(JVM 参数)

如果你需要处理来自控制台或文件的输入输出,并且系统默认编码不是 UTF-8(Windows 的默认编码是 GBK),你需要显式告诉 JVM 使用 UTF-8。

  • 启动时添加 JVM 参数

    java -Dfile.encoding=UTF-8 -jar your_app.jar
    • -Dfile.encoding=UTF-8 这个参数会设置 JVM 的默认文件编码,这对于 System.in, System.out, System.err 以及许多不指定字符集的 InputStreamReader/OutputStreamWriter 构造函数非常有效。

    注意:虽然这个参数很方便,但它并不总是万能的,最佳实践是在所有 I/O 操作中显式指定字符集。

I/O 操作中的显式指定字符集

这是最关键、最可靠的方法,无论环境如何,只要在读写时明确指定使用 StandardCharsets.UTF_8,就能保证编码正确。

文件读写

错误示范(依赖系统默认编码)

// 这种写法在不同系统上可能表现不同
try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
    // ...
}
try (BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))) {
    // ...
}

正确示范(显式指定 UTF-8)

import java.io.*;
import java.nio.charset.StandardCharsets;
// --- 读取文件 ---
try (BufferedReader reader = new BufferedReader(
        new InputStreamReader(new FileInputStream("data.txt"), StandardCharsets.UTF_8))) {
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line); // System.out 的编码也需要注意
    }
} catch (IOException e) {
    e.printStackTrace();
}
// --- 写入文件 ---
try (BufferedWriter writer = new BufferedWriter(
        new OutputStreamWriter(new FileOutputStream("output.txt"), StandardCharsets.UTF_8))) {
    writer.write("你好,世界!");
    writer.newLine();
    writer.write("Hello, World!");
} catch (IOException e) {
    e.printStackTrace();
}

控制台输入输出

  • System.outSystem.err: 它们的编码受到 JVM 启动时 -Dfile.encoding 参数的影响,如果未设置,它会使用操作系统的默认编码,为了确保正确输出,可以包装它们:

    // 包装 System.out 以确保 UTF-8 输出
    PrintSystemOut = new PrintStream(System.out, true, StandardCharsets.UTF_8);
    System.setOut(printSystemOut);
    // 之后所有的 System.out.println 都会使用 UTF-8
    System.out.println("这是中文输出");
  • Scanner 读取控制台输入Scanner 默认使用系统的默认编码,为了读取 UTF-8 输入,需要指定:

    import java.util.Scanner;
    import java.nio.charset.StandardCharsets;
    // 使用 System.in 的 UTF-8 包装版本
    Scanner scanner = new Scanner(System.in, StandardCharsets.UTF_8.name());
    System.out.print("请输入一些文字: ");
    String userInput = scanner.nextLine();
    System.out.println("你输入的是: " + userInput);
    scanner.close();

网络通信

在网络编程中,HTTP 协议通常使用 Content-Type 头部来指定内容的字符集。

  • 客户端发送请求:确保请求体(如果包含文本)是 UTF-8 编码。
  • 服务器端处理请求:从 Content-Type 头部读取字符集,并使用它来解码请求体,如果没有指定,则默认使用 ISO-8859-1(这是 HTTP/1.1 的规范),但这几乎肯定不是你想要的,现代框架(如 Spring Boot)通常会默认使用 UTF-8。
场景 错误/不推荐的做法 正确/推荐的做法
源代码文件 不设置,使用 IDE 默认编码(可能是 GBK)。 在 IDE 和 Maven/Gradle 中统一设置为 UTF-8
JVM 启动 不设置,依赖系统默认编码。 在生产环境启动脚本中添加 -Dfile.encoding=UTF-8 作为保障。
文件读写 new FileReader("file.txt"),不指定编码。 new InputStreamReader(new FileInputStream("file.txt"), StandardCharsets.UTF_8)
控制台输出 直接使用 System.out.println() 包装 System.out 或确保 JVM 参数已设置。
控制台输入 new Scanner(System.in),不指定编码。 new Scanner(System.in, StandardCharsets.UTF_8.name())
字符串编码转换 手动写 "a".getBytes("ISO-8859-1") 使用 StandardCharsets.UTF_8 常量,如 "a".getBytes(StandardCharsets.UTF_8)

核心思想“明确优于隐式”,永远不要依赖系统的默认编码,在你的 Java 项目中,从源码到 I/O,全程强制使用 UTF-8,并始终在需要编码/解码的地方显式地指定 StandardCharsets.UTF_8,这样可以最大程度地避免乱码问题。

分享:
扫描分享到社交APP
上一篇
下一篇