【java IO】字符流详解

Java IO 从入门到深入(第三篇):字符流 Reader / Writer 详解(含乱码问题)


在上一篇中,我们学习了:

  • 字节流 InputStream / OutputStream
  • 文件复制
  • IO 基本操作

但字节流有一个明显问题:

不适合直接处理文本

例如:

id="5jsjyz" 复制代码
中文乱码
字符截断
编码错误

因此 Java 提供了另一套 IO 体系:

字符流(Character Stream)

本篇重点:

  1. 什么是字符流
  2. 为什么需要字符流
  3. Reader / Writer 体系结构
  4. FileReader / FileWriter 使用
  5. 字符编码问题(重点)
  6. 常见乱码原因
  7. 最佳实践(避免乱码)
  8. 常见易错点
  9. 面试高频问题

一、什么是字符流?

字符流的核心特点:

以字符(char)为单位读写数据

单位:

id="fb6b7o" 复制代码
char(2字节)

适用于:

id="h2j2wt" 复制代码
文本文件
txt
json
xml
html

二、为什么需要字符流?

来看一个问题:

java 复制代码
FileInputStream fis = new FileInputStream("test.txt");

int data;

while((data = fis.read()) != -1){
    System.out.print((char)data);
}

如果文件内容是:

id="lfl0fi" 复制代码
你好

可能输出:

id="0b4krt" 复制代码
ä½ å¥½

原因:

id="b61ckb" 复制代码
字符编码问题

本质原因

字节流:

id="x9dycr" 复制代码
不理解字符编码

字符流:

id="k43vff" 复制代码
自动处理编码

三、字符流的核心类

字符流分为两大类:

id="r9p9s3" 复制代码
Reader
Writer

结构:

id="u4jjw3" 复制代码
字符流
│
├── Reader(读)
│
└── Writer(写)

四、Reader 体系结构

id="r2h4re" 复制代码
Reader
│
├── FileReader
├── BufferedReader
├── InputStreamReader
└── StringReader

常用:

id="j7gqk0" 复制代码
FileReader
BufferedReader

五、Writer 体系结构

id="6sru4d" 复制代码
Writer
│
├── FileWriter
├── BufferedWriter
├── OutputStreamWriter
└── StringWriter

六、FileReader 使用

示例:

java 复制代码
import java.io.FileReader;

public class Main {

    public static void main(String[] args) throws Exception {

        FileReader fr = new FileReader("test.txt");

        int data;

        while((data = fr.read()) != -1){
            System.out.print((char)data);
        }

        fr.close();
    }
}

批量读取(推荐)

java 复制代码
char[] buffer = new char[1024];

int len;

while((len = fr.read(buffer)) != -1){

    System.out.print(new String(buffer,0,len));
}

七、FileWriter 使用

写入文件:

java 复制代码
import java.io.FileWriter;

public class Main {

    public static void main(String[] args) throws Exception {

        FileWriter fw = new FileWriter("test.txt");

        fw.write("你好,Java IO");

        fw.close();
    }
}

追加写入

java 复制代码
FileWriter fw = new FileWriter("test.txt", true);

八、字符流 vs 字节流

对比 字节流 字符流
单位 byte char
处理数据 二进制 文本
是否处理编码
使用场景 图片/视频 文本

九、字符编码问题(重点)

这是 IO 中最重要的知识点之一。


常见编码

id="q0z71d" 复制代码
UTF-8
GBK
ISO-8859-1

编码与解码

id="90r2yt" 复制代码
编码:字符 → 字节
解码:字节 → 字符

乱码产生原因

本质:

id="0dgs6y" 复制代码
编码和解码不一致

例如:

id="1g4r0l" 复制代码
写入:UTF-8
读取:GBK

结果:

id="gxxc6n" 复制代码
乱码

十、解决乱码的正确方式(重点)

不要直接使用 FileReader / FileWriter!

推荐使用:

id="o5f9qe" 复制代码
InputStreamReader
OutputStreamWriter

正确示例

java 复制代码
import java.io.*;

public class Main {

    public static void main(String[] args) throws Exception {

        InputStreamReader reader =
                new InputStreamReader(
                        new FileInputStream("test.txt"),
                        "UTF-8"
                );

        int data;

        while((data = reader.read()) != -1){
            System.out.print((char)data);
        }

        reader.close();
    }
}

十一、try-with-resources 写法

推荐写法:

java 复制代码
try(FileReader fr = new FileReader("test.txt")){

    int data;

    while((data = fr.read()) != -1){
        System.out.print((char)data);
    }

}

十二、字符流使用场景

1 读取文本文件

id="8n39w7" 复制代码
配置文件
日志文件
JSON
XML

2 写日志

id="o6h7q3" 复制代码
日志系统

3 处理字符串数据

id="3joxn2" 复制代码
文本分析
数据解析

十三、常见易错点(非常重要)

1 FileReader 默认编码问题

id="yrkgum" 复制代码
使用系统默认编码

不同系统可能不同:

id="hhfd94" 复制代码
Windows → GBK
Linux → UTF-8

👉 导致乱码


2 忘记 close()

id="gg0k9k" 复制代码
资源泄露

3 read() 使用错误

错误:

java 复制代码
while(fr.read() != -1)

正确:

java 复制代码
int data;

while((data = fr.read()) != -1)

4 写入未 flush

id="klngw9" 复制代码
数据未写入文件

解决:

id="7l2drd" 复制代码
close()
或 flush()

十四、面试高频问题

1 字节流和字符流的区别

核心:

id="pfyr3c" 复制代码
是否处理编码

2 为什么会出现乱码?

答案:

id="8qv9ya" 复制代码
编码和解码不一致

3 FileReader 为什么不推荐?

原因:

id="5cslgk" 复制代码
无法指定编码

4 如何解决乱码?

标准答案:

id="d6xt5n" 复制代码
InputStreamReader + 指定编码

5 Java IO 如何处理中文?

id="rskz1l" 复制代码
统一使用 UTF-8

十五、最佳实践总结(非常重要)

推荐写法:

java 复制代码
try(
    BufferedReader br = new BufferedReader(
        new InputStreamReader(
            new FileInputStream("test.txt"), "UTF-8"
        )
    )
){
    String line;

    while((line = br.readLine()) != null){
        System.out.println(line);
    }
}

优点:

id="gktfr5" 复制代码
支持缓冲
支持编码
性能高
不乱码

十六、总结

本篇讲解了 字符流的核心内容

重点:

核心类

id="awud2n" 复制代码
Reader
Writer

常用实现

id="g3f4bf" 复制代码
FileReader
FileWriter

更推荐

id="0b6ywo" 复制代码
InputStreamReader
OutputStreamWriter

最重要知识点

id="17qg8g" 复制代码
字符编码
乱码问题
相关推荐
ZHOUPUYU2 小时前
PHP文件处理与安全实战——从上传下载到高级操作的完整指南
开发语言·php
小年糕是糕手2 小时前
【35天从0开始备战蓝桥杯 -- 补充包】
开发语言·前端·数据结构·数据库·c++·算法·蓝桥杯
Cc琎2 小时前
api接口分布在多台服务器, 如何同步用户的每日请求次数
java·运维·服务器·redis·php
Zzj_tju3 小时前
Java 从入门到精通(六):抽象类与接口到底怎么选?
java·开发语言
@PHARAOH4 小时前
HOW - Go 开发入门(一)
开发语言·后端·golang
美好的事情能不能发生在我身上10 小时前
Hot100中的:贪心专题
java·数据结构·算法
myloveasuka10 小时前
Java与C++多态访问成员变量/方法 对比
java·开发语言·c++
2301_8217005311 小时前
C++编译期多态实现
开发语言·c++·算法
Andya_net11 小时前
Spring | @EventListener事件机制深度解析
java·后端·spring