Java 基础 IO 流之字符流详解

在 Java 的 I/O 体系中,字符流(Character Stream) 是专门用于处理文本数据的流类型。与字节流不同,字符流以 16 位 Unicode 字符为单位进行读写,能够自动处理字符编码问题,是处理文本文件(如 .txt.java.csv 等)的首选方式。


一、为什么需要字符流?

虽然字节流可以读写任何文件,但在处理文本时存在明显缺陷:

  • 字节流无法自动识别字符编码(如 UTF-8、GBK)
  • 直接按字节读取中文等多字节字符容易出现乱码
  • 操作单位是 byte,不便于按“行”或“字符”处理文本

字符流正是为解决这些问题而设计:

  • 自动处理字符与字节之间的转换
  • 支持指定编码(推荐显式指定)
  • 提供按字符、按行读写等便捷方法

✅ 字符流适用于:日志文件、配置文件、源代码、CSV、JSON 等纯文本场景


二、字符流的核心类

Java 的字符流位于 java.io 包,核心抽象类如下:

抽象类 作用 常见子类
Reader 字符输入流:从源读取字符 FileReader, BufferedReader, InputStreamReader
Writer 字符输出流:向目标写入字符 FileWriter, BufferedWriter, OutputStreamWriter

📌 所有具体字符流都继承自这两个抽象类。


三、FileReader:从文本文件读取字符

FileReader 是最简单的字符输入流,用于读取本地文本文件

基本用法(不推荐)

import java.io.FileReader;
import java.io.IOException;

public class FileReaderExample {
    public static void main(String[] args) {
        try (FileReader fr = new FileReader("note.txt")) {
            int ch;
            while ((ch = fr.read()) != -1) {
                System.out.print((char) ch); // 按字符输出
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

⚠️ 问题:FileReader 使用系统默认编码(如 Windows 是 GBK,Linux 是 UTF-8),在跨平台或处理非默认编码文件时极易乱码!


四、FileWriter:向文本文件写入字符

FileWriter 用于将字符数据写入文件。

基本写入

import java.io.FileWriter;
import java.io.IOException;

public class FileWriterExample {
    public static void main(String[] args) {
        try (FileWriter fw = new FileWriter("output.txt")) {
            fw.write("你好,Java 字符流!");
            fw.write("\n这是第二行。");
            // close() 会自动 flush()
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

追加模式

// 第二个参数 true 表示追加
FileWriter fw = new FileWriter("log.txt", true);

⚠️ 同样问题:FileWriter 也使用系统默认编码,不推荐在生产环境中直接使用。


五、正确做法:使用 InputStreamReader / OutputStreamWriter(指定编码)

要避免乱码,应使用 InputStreamReaderOutputStreamWriter,它们允许显式指定字符编码

读取 UTF-8 文本文件

import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

public class ReadWithEncoding {
    public static void main(String[] args) {
        try (
            FileInputStream fis = new FileInputStream("utf8_file.txt");
            InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8);
            BufferedReader reader = new BufferedReader(isr)
        ) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

写入 UTF-8 文本文件

import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

public class WriteWithEncoding {
    public static void main(String[] args) {
        try (
            FileOutputStream fos = new FileOutputStream("output_utf8.txt");
            OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
            BufferedWriter writer = new BufferedWriter(osw)
        ) {
            writer.write("Hello, 世界!");
            writer.newLine(); // 跨平台换行
            writer.write("Java 字符流支持 Unicode。");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

✅ 推荐使用 StandardCharsets.UTF_8(Java 7+),避免字符串 "UTF-8" 拼写错误。


六、BufferedReader / BufferedWriter:高效文本处理

字符流常配合缓冲流使用,以提升性能并提供便捷方法。

BufferedReader 的优势

  • readLine():按行读取(自动处理换行符 \n, \r\n
  • 内部缓冲减少 I/O 次数
try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {
    String line;
    while ((line = br.readLine()) != null) {
        System.out.println("行: " + line);
    }
} // 自动关闭

BufferedWriter 的优势

  • write(String):直接写字符串
  • newLine():写入平台无关的换行符
  • flush():强制刷新缓冲区
try (BufferedWriter bw = new BufferedWriter(new FileWriter("log.txt"))) {
    bw.write("开始记录");
    bw.newLine();
    bw.write("时间: " + System.currentTimeMillis());
    // close() 会自动 flush()
}

七、字符流复制文本文件(带编码)

import java.io.*;
import java.nio.charset.StandardCharsets;

public class TextFileCopy {
    public static void copyTextFile(String src, String dest) {
        try (
            BufferedReader reader = new BufferedReader(
                new InputStreamReader(new FileInputStream(src), StandardCharsets.UTF_8)
            );
            BufferedWriter writer = new BufferedWriter(
                new OutputStreamWriter(new FileOutputStream(dest), StandardCharsets.UTF_8)
            )
        ) {
            String line;
            while ((line = reader.readLine()) != null) {
                writer.write(line);
                writer.newLine(); // 保留原换行格式
            }
            System.out.println("✅ 文本文件复制完成");
        } catch (IOException e) {
            System.err.println("❌ 复制失败: " + e.getMessage());
        }
    }

    public static void main(String[] args) {
        copyTextFile("source.txt", "copy.txt");
    }
}

🔍 注意:此方法会统一换行符为当前平台格式。若需保留原始换行符,应使用字节流复制。


八、字符流 vs 字节流(关键区别)

特性 字符流 字节流
数据单位 char(16 位 Unicode) byte(8 位)
适用数据 文本文件 所有文件(二进制/文本)
编码处理 自动转换(可指定) 无(原始字节)
核心类 Reader / Writer InputStream / OutputStream
典型场景 读写 .txt, .java, .json 读写 .jpg, .mp3, .zip

📌 黄金法则

  • 处理文本 → 用字符流(并指定编码!)
  • 处理二进制 → 用字节流

九、现代替代方案(Java 7+)

Java NIO.2 提供了更简洁的文本处理 API:

import java.nio.file.*;
import java.util.List;
import java.nio.charset.StandardCharsets;

// 读取所有行(List<String>)
List<String> lines = Files.readAllLines(
    Paths.get("data.txt"), 
    StandardCharsets.UTF_8
);

// 写入所有行
Files.write(
    Paths.get("output.txt"), 
    lines, 
    StandardCharsets.UTF_8
);

// 按行处理大文件(Stream)
try (Stream<String> stream = Files.lines(Paths.get("large.log"), StandardCharsets.UTF_8)) {
    stream.filter(line -> line.contains("ERROR"))
          .forEach(System.out::println);
}

✅ 优点:代码简洁、自动资源管理、内置编码支持、支持函数式编程。

但理解传统字符流仍是掌握 Java I/O 的基础。


十、最佳实践总结

  1. 永远不要直接使用 FileReader/FileWriter
    → 改用 InputStreamReader/OutputStreamWriter 并指定编码(如 UTF-8

  2. 文本处理务必使用缓冲流
    BufferedReaderreadLine())和 BufferedWriternewLine()

  3. 使用 try-with-resources 自动关闭流
    → 避免资源泄漏

  4. 明确区分文本与二进制数据
    → 文本用字符流,其他用字节流

  5. 优先考虑 NIO.2 的 Files 工具类
    → 对于简单文件操作,代码更简洁安全


结语

字符流是 Java 处理文本数据的“桥梁”,它屏蔽了底层字节与字符编码的复杂性,让我们能以“人类可读”的方式操作文件。掌握字符流的核心类、编码处理和缓冲机制,是编写健壮、国际化 Java 应用的关键一步。

🙌 感谢你读到这里!
🔍 技术之路没有捷径,但每一次阅读、思考和实践,都在悄悄拉近你与目标的距离。
💡 如果本文对你有帮助,不妨 👍 点赞、📌 收藏、📤 分享 给更多需要的朋友!
💬 欢迎在评论区留下你的想法、疑问或建议,我会一一回复,我们一起交流、共同成长 🌿
🔔 关注我,不错过下一篇干货!我们下期再见!✨

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐