Skip to content

字节流

概述

字节流字节(byte)为单位读写数据,是 Java IO 中最底层的流式抽象。InputStreamOutputStream 是字节流的两个抽象基类:前者表示输入源(如文件、网络、内存),后者表示输出目标。字节流适用于任意二进制数据(如图片、音频、视频、压缩包)以及按字节处理的文本;若主要处理文本且需要按字符/行处理,应使用 字符流(Reader/Writer)。

掌握字节流是学习 缓冲流字符流NIO 的基础。


字节流体系概览

类型常用实现类说明
InputStreamFileInputStreamByteArrayInputStreamBufferedInputStream从文件、字节数组、缓冲等读取字节
OutputStreamFileOutputStreamByteArrayOutputStreamBufferedOutputStream向文件、字节数组、缓冲等写入字节

FileInputStream / FileOutputStream 直接绑定文件,是文件读写最常用的字节流实现。使用前需通过 File 类 或路径字符串指定文件位置。


基本用法

InputStream 核心方法

方法说明
int read()读取一个字节,返回 0–255;若已到末尾返回 -1
int read(byte[] b)将字节读入数组 b,返回实际读取的字节数;末尾返回 -1
int read(byte[] b, int off, int len)将最多 len 个字节读入 b,从 off 开始存放;末尾返回 -1
void close()关闭流并释放资源,必须调用

read(byte[] b) 会尽可能把 b.length 个字节读入数组,返回值可能小于数组长度(例如文件剩余字节不足时)。

OutputStream 核心方法

方法说明
void write(int b)写出一个字节(低 8 位有效)
void write(byte[] b)写出整个字节数组
void write(byte[] b, int off, int len)写出数组 b 中从 off 起的 len 个字节
void flush()刷新缓冲(部分实现类有缓冲时有效)
void close()关闭流并释放资源,必须调用

说明

FileInputStream / FileOutputStreamreadwrite 会抛出受检异常 IOException,必须用 try-catch 捕获或在方法上 throws IOException。推荐用 try-with-resources 自动关闭流。详见 异常处理


使用示例

示例 1:用 FileInputStream 读取文件(逐字节)

java
import java.io.FileInputStream;
import java.io.IOException;

public class ReadFileByByte {
    public static void main(String[] args) {
        // try-with-resources 确保流在结束时自动关闭,避免资源泄漏
        try (FileInputStream in = new FileInputStream("data/hello.txt")) {
            int b;
            while ((b = in.read()) != -1) {
                // 读到的是字节(0-255),若文件是文本可强转为 char 查看(需注意编码)
                System.out.print((char) b);
            }
        } catch (IOException e) {
            System.err.println("读取失败: " + e.getMessage());
        }
    }
}

逐字节 read() 适合小文件或仅需「扫一遍」的场景;大文件更推荐按块读取(见示例 2)。

示例 2:按字节数组读取(推荐)

java
import java.io.FileInputStream;
import java.io.IOException;

public class ReadFileByBuffer {
    public static void main(String[] args) {
        byte[] buffer = new byte[8192];  // 8KB 一块,按块读减少系统调用次数
        try (FileInputStream in = new FileInputStream("data/hello.txt")) {
            int len;
            while ((len = in.read(buffer)) != -1) {
                // len 为本轮实际读到的字节数,可能小于 buffer.length
                processBytes(buffer, 0, len);
            }
        } catch (IOException e) {
            System.err.println("读取失败: " + e.getMessage());
        }
    }

    private static void processBytes(byte[] b, int off, int len) {
        // 例如:写入另一流、解析协议、或按需转成字符串(需指定编码)
        System.out.write(b, off, len);
    }
}

提示

大文件读写应使用 read(byte[] b) / write(byte[] b) 配合合理大小的数组(如 4KB–64KB),避免逐字节导致性能差。需要更高性能时可使用 缓冲流

示例 3:用 FileOutputStream 写入文件

java
import java.io.FileOutputStream;
import java.io.IOException;

public class WriteFileDemo {
    public static void main(String[] args) {
        // 第二个参数 true 表示追加,默认 false 会覆盖原文件
        try (FileOutputStream out = new FileOutputStream("output/result.bin", false)) {
            byte[] data = { 0x48, 0x65, 0x6C, 0x6C, 0x6F };  // "Hello" 的 ASCII
            out.write(data);
            out.write('\n');
            // FileOutputStream 一般无需 flush,close 时会写入
        } catch (IOException e) {
            System.err.println("写入失败: " + e.getMessage());
        }
    }
}

示例 4:文件复制(字节流 + try-with-resources)

java
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class CopyFileDemo {
    public static void copy(String src, String dest) throws IOException {
        try (FileInputStream in = new FileInputStream(src);
             FileOutputStream out = new FileOutputStream(dest)) {
            byte[] buffer = new byte[8192];
            int len;
            while ((len = in.read(buffer)) != -1) {
                out.write(buffer, 0, len);  // 只写出本轮读到的 len 个字节
            }
        }
    }

    public static void main(String[] args) {
        try {
            copy("data/source.dat", "output/copy.dat");
            System.out.println("复制完成");
        } catch (IOException e) {
            System.err.println("复制失败: " + e.getMessage());
        }
    }
}

注意

read(buffer) 返回的 len 可能小于 buffer.length,写盘时必须用 write(buffer, 0, len),若写成 write(buffer) 会把数组中未读入的旧数据也写出,造成错误。


注意事项

注意

字节流操作会抛出受检异常 IOException(如文件不存在、无权限、磁盘满)。必须用 try-catch 处理或在方法上 throws IOException,否则无法通过编译。

注意

必须关闭流。未关闭的流会占用文件句柄和系统资源,导致「打开文件过多」等错误。推荐使用 try-with-resources,在 try 块结束时自动调用 close()

易错点说明
read() 返回值read() 返回 int:0–255 表示一个字节,-1 表示结束;不要当作 byte 直接比较。
write(buffer) 与 len按块读写时,写出时要用 write(buffer, 0, len),不要写出整个数组。
文本与编码字节流不处理字符编码;处理文本文件且需按字符/行操作时,应使用 字符流
覆盖与追加FileOutputStream(path) 会覆盖原文件;FileOutputStream(path, true) 为追加模式。

相关链接

基于 VitePress 构建