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流。

相关推荐
小羊没烦恼!19 小时前
微服务化的基石——持续集成
java·大数据·word·powerpoint·.net
一隅论数智19 小时前
给AI一张“业务概念地图“:本体如何从哲学走向企业智能
大数据·人工智能·经验分享·笔记·学习·学习方法·政务
俊昭喜喜里19 小时前
java中的继承和多态的区别
java
小羊没烦恼!19 小时前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
XiHongShi201619 小时前
STM32F407 RTC定时器例程,建议保存
stm32·单片机·学习
譕痕19 小时前
JSONObject与JSONArray封装数据格式区别
java·json
胡写代码20 小时前
别再前后端各写一套表单校验了
java·后端
小鱼能吃糖20 小时前
缺陷修复总览 · mall电商项目:5类缺陷,1个病根,4个业务域
java·电商
爱吃苹果的日记本20 小时前
离散数学第六课
学习·离散数学