【java IO】字节流详解

Java IO 从入门到深入(第二篇):字节流 InputStream / OutputStream 详解

在上一篇文章中,我们介绍了:

  • Java IO 整体体系
  • File 类的使用

File 只能 表示文件路径,并不能真正读取或写入文件内容。

如果想操作文件数据,就必须使用 IO流(Stream)

本篇我们将进入 Java IO 最核心的一部分:

字节流(Byte Stream)

本文主要内容:

  1. 什么是字节流
  2. InputStream / OutputStream 体系结构
  3. 常用字节流类
  4. 文件读取示例
  5. 文件写入示例
  6. 文件复制案例(高频)
  7. try-with-resources(现代写法)
  8. 常见易错点
  9. 面试高频问题

一、什么是字节流?

字节流是 Java IO 中最基础的一种流。

特点:

以字节(byte)为单位进行数据读写

适用于:

复制代码
图片
视频
音频
压缩包
二进制文件

例如:

复制代码
jpg
png
mp4
zip
exe

这些文件本质上都是 二进制数据

因此 Java 使用 字节流处理它们


二、Java 字节流的核心类

Java 字节流分为两大类:

复制代码
InputStream
OutputStream

结构:

复制代码
字节流
│
├── InputStream
│      文件读取
│
└── OutputStream
       文件写入

简单理解:

作用
InputStream 读取数据
OutputStream 写入数据

三、InputStream 体系结构

InputStream 是所有字节输入流的 抽象父类

常见子类:

复制代码
InputStream
│
├── FileInputStream
├── BufferedInputStream
├── ByteArrayInputStream
└── ObjectInputStream

其中最常用的是:

FileInputStream

用于 读取文件数据


四、OutputStream 体系结构

OutputStream 是所有字节输出流的 抽象父类

结构:

复制代码
OutputStream
│
├── FileOutputStream
├── BufferedOutputStream
├── ByteArrayOutputStream
└── ObjectOutputStream

最常用:

FileOutputStream

用于 写入文件数据


五、InputStream 常用方法

read()

java 复制代码
int read()

作用:

复制代码
读取一个字节

返回值:

返回值 含义
0~255 读取到的字节
-1 文件结束

示例:

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

int data;

while((data = fis.read()) != -1){
    System.out.print((char)data);
}

fis.close();

六、批量读取(推荐)

逐字节读取效率较低。

推荐:

java 复制代码
read(byte[])

示例:

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

byte[] buffer = new byte[1024];

int len;

while((len = fis.read(buffer)) != -1){

    String str = new String(buffer,0,len);
    System.out.print(str);

}

fis.close();

解释:

复制代码
缓冲区 (Buffer):byte[1024]就是一个临时的"数据容器",一次读取多个字节,减少磁盘访问次数,提升效率。
实际长度 (len):fis.read(buffer)的返回值。它表示本次实际读入了多少字节。文件末尾时返回 -1。这个值至关重要,因为缓冲区最后一块可能没被填满。
循环处理:只要没读到文件尾 (len != -1),就进入循环。
正确转换:new String(buffer, 0, len)用 len确保只将本次读入的有效字节转换成字符串,避免处理缓冲区末尾的"旧数据"。
一句话流程:用一个缓冲区反复批量读取文件,每次只处理实际读到的数据,读完为止。

流程模拟(读取一个1500字节的文件)

循环次数 buffer状态 (大小1024) len的值 (实际读取字节数) 转换字符串的参数 说明
第1次 被文件前1024字节填满 1024 new String(buffer, 0, 1024) 读取并处理了前1024字节。
第2次 被文件剩下476字节 覆盖。 前476字节是新数据,后面548字节是上次的旧数据 476 new String(buffer, 0, 476) 只处理新的476字节,忽略缓冲区后面的旧数据。
第3次 无新数据,read返回-1 -1 不进入循环 文件结束,循环退出。

七、OutputStream 常用方法

write(int b)

写入一个字节:

java 复制代码
fos.write(65);

写入字符:

复制代码
A

write(byte[])

写入字节数组:

java 复制代码
String str = "Hello Java";

fos.write(str.getBytes());

八、写入文件示例

完整示例:

java 复制代码
import java.io.FileOutputStream;

public class Main {

    public static void main(String[] args) throws Exception {

        FileOutputStream fos = new FileOutputStream("test.txt");

        String text = "Hello Java IO";

        fos.write(text.getBytes());

        fos.close();
    }

}

执行后:

复制代码
test.txt

内容:

复制代码
Hello Java IO

九、文件追加写入

默认:

复制代码
覆盖文件

如果需要 追加写入

java 复制代码
FileOutputStream fos = new FileOutputStream("test.txt", true);

示例:

java 复制代码
fos.write("Hello".getBytes());

十、经典案例:文件复制(面试高频)

文件复制是 Java IO 面试经典题

示例:

java 复制代码
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class CopyFile {

    public static void main(String[] args) throws Exception {

        FileInputStream fis = new FileInputStream("a.jpg");

        FileOutputStream fos = new FileOutputStream("b.jpg");

        byte[] buffer = new byte[1024];

        int len;

        while((len = fis.read(buffer)) != -1){

            fos.write(buffer,0,len);

        }

        fis.close();
        fos.close();
    }

}

核心逻辑:

复制代码
读 → 写
读 → 写
读 → 写

直到:

复制代码
read() = -1

十一、try-with-resources(推荐写法)

传统写法:

复制代码
需要手动 close()

现代 Java 推荐:

复制代码
try-with-resources

示例:

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

    int data;

    while((data = fis.read()) != -1){
        System.out.print((char)data);
    }

}

优势:

复制代码
自动关闭资源
代码更安全

十二、字节流的使用场景

字节流适合:

1 文件复制

例如:

复制代码
图片
视频
压缩包

2 文件下载

Web系统下载:

复制代码
浏览器 ← 服务器

3 上传文件

例如:

复制代码
MultipartFile

底层也是字节流。


十三、常见易错点

1 忘记关闭流

错误:

复制代码
资源泄露

推荐:

复制代码
try-with-resources

2 read() 返回值

很多人写:

java 复制代码
while(fis.read() != -1)

但没有保存返回值。

正确:

java 复制代码
int data;

while((data = fis.read()) != -1)

3 写入 buffer 错误

错误写法:

java 复制代码
fos.write(buffer);

正确:

java 复制代码
fos.write(buffer,0,len);

否则会写入 脏数据


十四、面试高频问题

1 InputStream 和 Reader 的区别

类型 单位
InputStream byte
Reader char

2 为什么 read() 返回 int?

原因:

复制代码
byte = -128 ~ 127
而一个字节范围是0 ------ 255,用bute注定无法使用-1为结束

但:

复制代码
-1 表示结束

因此必须使用 int


3 Java IO 如何提高性能?

方法:

复制代码
缓冲流
BufferedInputStream
BufferedOutputStream

4 Java IO 如何避免资源泄露?

推荐:

复制代码
try-with-resources

十五、总结

本篇介绍了 Java IO 字节流

核心类:

复制代码
InputStream
OutputStream

最常用实现:

复制代码
FileInputStream
FileOutputStream

核心方法:

复制代码
read()
read(byte[])

write()
write(byte[])

经典应用:

复制代码
文件读取
文件写入
文件复制
相关推荐
qq_367719302 小时前
Android MQTT开源库paho.mqtt.android+MQTTX软件使用记录
android·java·开源·android mqtt开源库·mqttx软件使用
智算菩萨2 小时前
AI赋能游戏创作:基于ChatGPT 5.4的小游戏智能设计与开发全解析
人工智能·python·ai·chatgpt·ai编程
游戏开发爱好者82 小时前
如何使用Instruments和Keymob进行Swift应用性能优化分析
开发语言·ios·性能优化·小程序·uni-app·iphone·swift
Mem0rin2 小时前
[Java面向对象]接口的声明和实现继承
java·开发语言
阿猿收手吧!2 小时前
【C++】深入解析日志框架调用链
开发语言·c++
m0_528174452 小时前
多平台UI框架C++开发
开发语言·c++·算法
jing-ya2 小时前
day 53 图论part5
java·数据结构·算法·图论
haokan_Jia2 小时前
postgresql实现数据动态地图切片服务
java·服务器·前端
014-code2 小时前
Spring Boot 自动配置原理深度解析
java·spring boot·后端·spring