Java IO 完整学习笔记|字节流、字符流、缓冲流、资源关闭全梳理

一、IO流整体体系

Java IO两大分支:字节流 、字符流 ,所有流都实现AutoCloseable接口,支持try‑with‑resources自动关闭。

分类 输入(读) 输出(写) 处理单元
字节流 InputStream OutputStream byte 字节
字符流 Reader Writer char 字符

1. 字节流:万能二进制流

父类:InputStream / OutputStream 常用实现:FileInputStream、FileOutputStream

✅ 适用:所有文件:图片、视频、压缩包、文档。直接操作原始二进制,不会做编码转换。

核心API
java 复制代码
//读:返回int,-1代表读到文件末尾
int read();                  //读取1个字节
int read(byte[] buffer);     //批量读到字节数组,返回读到字节数

//写
void write(int b);                     //写单个字节
void write(byte[] b);                  //写整个字节数组
void write(byte[] b,int off,int len);  //数组指定范围写入

⚠️重点:read()返回int而不是byte 字节范围‑128~127,如果读到字节0xFF,强转byte等于‑1,会和文件结束标记‑1冲突。所以用int接收,高24位补0,用‑1代表流结束。
FileOutputStream构造第二个参数:true=追加写入;不传/false=覆盖原文件。

2. 字符流:专门处理文本

父类:Reader / Writer 常用实现:FileReader、FileWriter

✅适用:纯文本文件 (.txt、.java、.md) ❌禁止处理图片视频等二进制文件,会直接损坏文件。

底层本质:字符流 = 字节流 + 编码解码,把字节按照字符集转成char。

核心API
java 复制代码
int read();                 //读1个字符,返回‑1结束
int read(char[] cbuf);      //批量读到字符数组

void write(int c);
void write(char[] cbuf);
void write(String str);

⚠️字符流自带内存缓冲区! 数据先写到内存缓冲区,没有调用flush()或者close(),数据不会落到磁盘,会出现文件空白、数据丢失。

  • flush():强制把缓冲区数据刷到磁盘
  • close():关闭流,关闭前自动执行flush

3. 转换流:解决编码问题

FileReader / FileWriter会直接使用系统默认编码,跨平台会乱码。想要手动指定编码,使用转换流。

  • InputStreamReader:字节输入流 → 字符输入流,指定编码读文件
  • OutputStreamWriter:字节输出流 → 字符输出流,指定编码写文件
java 复制代码
//示例:指定UTF‑8读取文本
try(InputStreamReader isr = new InputStreamReader(new FileInputStream("test.txt"), StandardCharsets.UTF_8)){

}

二、缓冲流:提升读写性能

原始FileXXX流读写频繁磁盘IO,性能差。缓冲流增加内存缓冲区,减少磁盘交互,大幅提升效率,属于包装流,必须包裹原始流。

字节缓冲流 字符缓冲流
BufferedInputStream BufferedReader
BufferedOutputStream BufferedWriter
java 复制代码
//字节缓冲流示例
try(BufferedInputStream bis = new BufferedInputStream(new FileInputStream("test.jpg"))){

}

BufferedReader特有方法:readLine(),直接读取一行字符串,非常适合文本处理。 BufferedWriter特有方法:newLine(),写入换行符。

缓冲流底层:默认缓冲区大小8192字节,数据先填满缓冲区,再一次性操作磁盘,减少IO次数。

三、资源关闭:两种写法

流不关闭会造成文件句柄泄漏,程序长时间运行打开大量文件,服务直接崩溃。

1. 传统 try‑finally

java 复制代码
FileInputStream fis = null;
try {
    fis = new FileInputStream("a.txt");
    //读写操作
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if(fis != null){
        fis.close();
    }
}

缺陷:try块和finally块同时抛出异常,原始异常会被掩盖丢失。

2. Java7+ try‑with‑resources【推荐】

只要实现AutoCloseable接口,在try()括号中定义流对象,代码结束自动关闭资源,不需要手动finally。

java 复制代码
try(FileInputStream fis = new FileInputStream("a.txt");
    BufferedInputStream bis = new BufferedInputStream(fis)){
    //读写
}catch (IOException e){
    e.printStackTrace();
}

✅代码简洁、自动释放、不会丢失原始异常,开发优先使用。

❌误区:return不会关闭流!return只是方法返回,不会释放文件句柄。

四、字节流 vs 字符流完整对比

对比维度 字节流 字符流
顶层抽象 InputStream / OutputStream Reader / Writer
操作单元 byte字节 char字符
底层原理 直接读写二进制 字节流+编码集转换
自带缓冲区 无(缓冲流额外包装) 自带缓冲区
flush行为 不需要flush 必须flush/close落盘
适用文件 图片、视频、压缩包,全部文件 txt、源码等纯文本
乱码风险 无编码转换,不会乱码 编码不匹配直接乱码

五、总结

  1. read()返回值是int,判断结束条件==‑1,不要强转byte/char再判断。
  2. 字符流忘记flush/close,文件为空,数据丢失。
  3. FileWriter追加模式:构造传入第二个参数true,否则每次覆盖旧文件。
  4. FileReader/FileWriter跟随系统编码,跨平台乱码,指定编码优先用转换流。
  5. 不要拿字符流读取二进制文件,文件直接损坏。
  6. 流资源务必释放,优先try‑with‑resources,杜绝句柄泄漏。
  7. 缓冲流是包装流,关闭外层缓冲流即可,内层被包装流会自动关闭。
  8. 文件路径:Windows路径需要转义D:\\demo.txt,也可以写D:/demo.txt。

六、补充:File类

File类不负责文件内容读写,只用来表示文件/目录路径,完成创建、删除、重命名、判断是否存在、遍历目录等操作。 常用方法:

  • exists() 判断文件是否存在
  • createNewFile() 创建文件
  • mkdir() / mkdirs() 创建单级/多级目录
  • delete() 删除文件
  • listFiles() 遍历目录下全部子文件

File不能读写文件内容,读写必须依靠IO流。

相关推荐
十二同学啊3 分钟前
Python 工程化详解:项目结构、依赖管理、代码质量、测试与部署
java·开发语言·python
YIN_尹7 分钟前
【git原理与使用】git基本操作及底层原理(一)
java·git·elasticsearch
乐橙开放平台9 分钟前
跨地域监控难管理?通过 GB28181 把各地海康大华乐橙摄像头注册到云端平台
开发语言·笔记·物联网·自动化·php·音视频·智能家居
动词ing9 分钟前
【学习笔记】51单片机 模块化编程
笔记·学习·51单片机
程序员飞哥16 分钟前
AI 编程额度怎么用才不浪费?
java·前端·后端
yh弓长21 分钟前
Spring MVC 的请求链路
java·spring·mvc
拆房老料22 分钟前
【第四部分:Agentic 智能体设计模式】17. Agent 路由模式详解:从意图识别到动态模型选择
java·设计模式·ai编程·策略模式
海绵宝宝转agent30 分钟前
计网基础知识
网络·笔记·学习·智能路由器
樱花落木兰35 分钟前
Redis 消息队列的可靠性演进:从 List、Pub/Sub 到 Stream 消费者组,以及 8.x 版本改变了什么
java·开发语言·经验分享·redis·分布式·python·软件工程
ACP广源盛1392462567336 分钟前
Type‑C 扩展坞选型笔记:USB3.0 双通道信号切换方案 LH3412@ACP 评估
笔记·硬件架构·硬件工程·国产芯片·开关