Java IO流:从字节到字符的桥梁

Java IO流:从字节到字符的桥梁

1. 引入:为什么需要IO流?

程序运行时,数据都在内存中。但我们需要:

  • 把数据永久保存到文件(硬盘)
  • 从网络接收数据
  • 读取配置文件

IO流就是用来处理数据传输的。I(Input)输入,O(Output)输出。数据像水一样"流动",所以叫流。

1.1 流的分类思维导图

复制代码
                        IO流
                         |
        ┌────────────────┴────────────────┐
        │                                  │
    字节流                              字符流
(处理二进制数据)                      (处理文本数据)
        |                                  |
   ┌────┴────┐                        ┌────┴────┐
   │         │                        │         │
输入流    输出流                    输入流    输出流
InputStream OutputStream          Reader    Writer

2. 字节流:一切的基础

字节流可以处理任何类型的数据(图片、视频、音频、文本),因为它以字节(8位)为单位。

2.1 字节输出流:OutputStream

FileOutputStream:将数据写入文件。

java 复制代码
// 1. 创建流(指定文件)
FileOutputStream fos = new FileOutputStream("a.txt");  // 覆盖写入
FileOutputStream fos2 = new FileOutputStream("a.txt", true); // 追加写入

// 2. 写入数据
fos.write(97);        // 写入一个字节('a')
byte[] bytes = {97, 98, 99};
fos.write(bytes);     // 写入字节数组
fos.write(bytes, 1, 2); // 写入从索引1开始的2个字节

// 3. 关闭流(重要!)
fos.close();

2.2 字节输入流:InputStream

FileInputStream:从文件读取数据。

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

// 方式1:单个字节读取(效率低)
int b;
while ((b = fis.read()) != -1) {  // -1表示文件结束
    System.out.print((char) b);
}

// 方式2:批量读取(效率高)
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1) {
    System.out.println(new String(buffer, 0, len));
}

fis.close();

2.3 字节流复制文件示例

java 复制代码
// 文件复制(万能)
try (FileInputStream fis = new FileInputStream("source.jpg");
     FileOutputStream fos = new FileOutputStream("dest.jpg")) {
    byte[] buffer = new byte[8192];
    int len;
    while ((len = fis.read(buffer)) != -1) {
        fos.write(buffer, 0, len);
    }
} catch (IOException e) {
    e.printStackTrace();
}

3. 字符流:处理文本更高效

字符流以字符(16位)为单位,专门处理文本文件,避免中文乱码。

3.1 字符输出流:Writer

FileWriter:写入文本文件。

java 复制代码
FileWriter fw = new FileWriter("b.txt");

fw.write('你');           // 写入单个字符
fw.write("你好世界");      // 写入字符串
fw.write("你好Java", 0, 2); // 写入字符串前2个字符

fw.close();

3.2 字符输入流:Reader

FileReader:读取文本文件。

java 复制代码
FileReader fr = new FileReader("b.txt");

// 单个字符读取
int ch;
while ((ch = fr.read()) != -1) {
    System.out.print((char) ch);
}

// 字符数组读取
char[] buffer = new char[1024];
int len;
while ((len = fr.read(buffer)) != -1) {
    System.out.print(new String(buffer, 0, len));
}

fr.close();

3.3 处理中文乱码

FileReader/FileWriter使用默认编码 (IDE或系统编码),跨平台可能乱码。

同一台电脑上的两个程序(编辑器 vs 终端)默认编码不同,导致乱码。
解决方案:使用转换流指定编码。

java 复制代码
// 指定UTF-8编码读取
InputStreamReader isr = new InputStreamReader(
    new FileInputStream("c.txt"), "UTF-8");

// 指定GBK编码写入
OutputStreamWriter osw = new OutputStreamWriter(
    new FileOutputStream("d.txt"), "GBK");

字符字节流区别:

特性 字节流 字符流
基本单位 字节(8 bit) 字符(16 bit Unicode)
编码处理 无,需手动处理 自动使用字符集编解码
适用数据 任何类型(二进制 + 文本) 仅文本
抽象基类 InputStream / OutputStream Reader / Writer
典型应用 图片、视频、音频、网络传输 文本文件读写、控制台输入输出

4. 缓冲流:提升性能

缓冲流内部维护一个缓冲区,减少实际读写次数,大幅提升效率。

4.1 字节缓冲流

java 复制代码
// 输入
BufferedInputStream bis = new BufferedInputStream(
    new FileInputStream("large.jpg"));

// 输出
BufferedOutputStream bos = new BufferedOutputStream(
    new FileOutputStream("copy.jpg"));

byte[] buffer = new byte[8192];
int len;
while ((len = bis.read(buffer)) != -1) {
    bos.write(buffer, 0, len);
}
bis.close();
bos.close();

4.2 字符缓冲流

特有方法:readLine()newLine()

java 复制代码
// 读取文本行
BufferedReader br = new BufferedReader(
    new FileReader("poem.txt"));
String line;
while ((line = br.readLine()) != null) {
    System.out.println(line);
}
br.close();

// 写入文本行(自动换行)
BufferedWriter bw = new BufferedWriter(
    new FileWriter("output.txt"));
bw.write("第一行");
bw.newLine();   // 跨平台的换行
bw.write("第二行");
bw.close();

5. 转换流:字节→字符的转换

作用
InputStreamReader 字节输入流 → 字符输入流(指定编码)
OutputStreamWriter 字符输出流 → 字节输出流(指定编码)
java 复制代码
// 读取GBK编码的文件
InputStreamReader isr = new InputStreamReader(
    new FileInputStream("gbk.txt"), "GBK");

// 写入UTF-8编码的文件
OutputStreamWriter osw = new OutputStreamWriter(
    new FileOutputStream("utf8.txt"), "UTF-8");

6. 对象流:序列化与反序列化

6.1 概念

  • 序列化:把对象变为字节序列(保存到文件或网络传输)
  • 反序列化:把字节序列恢复为对象

6.2 使用步骤

java 复制代码
// 类必须实现Serializable接口
class Student implements Serializable {
    private static final long serialVersionUID = 1L; // 版本号
    private String name;
    private transient int age;  // transient:不序列化
}

// 序列化
ObjectOutputStream oos = new ObjectOutputStream(
    new FileOutputStream("student.obj"));
oos.writeObject(new Student("张三", 18));
oos.close();

// 反序列化
ObjectInputStream ois = new ObjectInputStream(
    new FileInputStream("student.obj"));
Student s = (Student) ois.readObject();
ois.close();

6.3 注意事项

要点 说明
Serializable 标记接口,没有方法
serialVersionUID 版本控制,修改类后能兼容
transient 修饰的字段不序列化
静态变量 不序列化(属于类)

7. IO流对比总结

分类 输入流 输出流 适用场景
字节流 InputStream OutputStream 图片、视频、音频
字符流 Reader Writer 文本文件
缓冲字节流 BufferedInputStream BufferedOutputStream 大文件字节复制
缓冲字符流 BufferedReader BufferedWriter 大文本处理(readLine)
转换流 InputStreamReader OutputStreamWriter 指定编码读写
对象流 ObjectInputStream ObjectOutputStream 对象序列化

8. 易错点总结

易错点 错误原因 正确做法
忘记关闭流 资源泄漏 try-with-resources或finally中close
字节流读中文乱码 汉字占多个字节 用字符流或指定编码
文件路径错误 相对路径基准不明 使用绝对路径或ClassLoader
read(byte[])返回值忽略 -1判断错误 用len记录实际读取长度
字符数组转String不使用长度 多读空字符 new String(buffer, 0, len)
flush忘记调用 数据没真正写入 缓冲流调用flush()或关闭流
相关推荐
hhzz1 小时前
第1天:初识Python
开发语言·python·学习编程
江沉晚呤时1 小时前
C# 运行时类型创建:深入探索动态类型生成技术
开发语言·c#
月落归舟1 小时前
深入剖析乐观锁背后的原理
java·乐观锁
SimonKing2 小时前
OpenCode 在 IDEA 中使用 ACP 协议 VS 直接使用 TUI,哪个编程方式更是你的菜?
java·后端·程序员
NE_STOP2 小时前
Redis--持久化之AOF
java
budingxiaomoli2 小时前
注册中心的其他实现-Nacos
java·spring cloud·微服务
大大大大晴天️2 小时前
Flink技术实践-Flink重启策略选型指南
java·大数据·flink
szial2 小时前
Python Click 教程:从函数到专业命令行工具
开发语言·python
Karle_2 小时前
为AI编辑器准备c++编译环境,onnxruntime、cmake、cl,网上坑太多备份记录后续方便使用。
开发语言·c++·编辑器