Java IO 从入门到深入(第三篇):字符流 Reader / Writer 详解(含乱码问题)
在上一篇中,我们学习了:
- 字节流 InputStream / OutputStream
- 文件复制
- IO 基本操作
但字节流有一个明显问题:
不适合直接处理文本
例如:
id="5jsjyz"
中文乱码
字符截断
编码错误
因此 Java 提供了另一套 IO 体系:
字符流(Character Stream)
本篇重点:
- 什么是字符流
- 为什么需要字符流
- Reader / Writer 体系结构
- FileReader / FileWriter 使用
- 字符编码问题(重点)
- 常见乱码原因
- 最佳实践(避免乱码)
- 常见易错点
- 面试高频问题
一、什么是字符流?
字符流的核心特点:
以字符(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"
字符编码
乱码问题