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

一、IO流整体体系

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

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

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

父类:InputStream / OutputStream 常用实现:FileInputStreamFileOutputStream

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

核心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 常用实现:FileReaderFileWriter

✅适用:纯文本文件.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流。

相关推荐
L-岁月染过的梦13 分钟前
把常用开发小工具收进 IDEA:Develop Helper 插件介绍
java·数据库·intellij-idea
雄哥00716 分钟前
java代码反编译CFR
java·开发语言·反编译·cfr
lsylalalala20 分钟前
多线程(2)
java·开发语言·多线程
星恒随风22 分钟前
C++11详解(一):统一初始化——列表初始化与 initializer_list
c++·笔记·学习·list·状态模式
lhldsg25 分钟前
酒吧点餐小程序开发:从需求分析到技术落地实战
java·前端·小程序·架构·交友
用户31268748772028 分钟前
volatile 到底能保证什么?JMM 与内存屏障深度剖析
java
金立基包装胶水31 分钟前
纸袋热封胶除了粘不牢,还有哪些隐性问题?
大数据·笔记·其他
我爱cope32 分钟前
【计算机网络 | 网络层补充:内部网关协议:RIP 与 OSPF 如何在自治系统内选择路由?】
网络·学习·计算机网络·智能路由器
是隼人39 分钟前
buuctf-pwn ciscn_2019_n_5题解(学习过程持续 更新)
c语言·学习·安全·pwn入门·ctf入门