Wrayの知识库 Wrayの知识库
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
  • Java章节编写规范
  • Java基础

  • Java集合

  • Java并发

  • Java IO

    • Java IO概述
    • IO基础

      • IO流体系
      • 字节流
        • 1. 字节流的核心类
        • 2. 读取字节文件
        • 3. 写入字节文件
        • 4. 文件复制示例
        • 5. 字节流适合什么场景
        • 6. 单字节读取的问题
        • 专家实践清单
        • Tips 快问快答
        • 7. 总结
      • 字符流
      • 字节流与字符流的转换
      • 装饰器模式与IO设计
    • 常用IO流

    • 文件与目录操作

    • Java NIO

    • 高级主题

    • 实践与性能优化

    • 面试与设计题

    • IO实践速查

  • JVM

目录

字节流

字节流是 Java IO 中最基础、最通用的一类流。计算机中的文件、网络数据、图片、视频、压缩包最终都可以看作字节序列,因此字节流可以处理几乎所有类型的数据。

# 1. 字节流的核心类

字节流的两个顶层抽象类是:

  • InputStream:字节输入流,从外部读取字节到程序。
  • OutputStream:字节输出流,从程序写出字节到外部。

常用方法如下:

int read()
int read(byte[] b)
int read(byte[] b, int off, int len)

void write(int b)
void write(byte[] b)
void write(byte[] b, int off, int len)
void flush()
void close()

read() 返回 -1 表示已经读取到流的末尾。

# 2. 读取字节文件

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

public class ReadBytesDemo {
    public static void main(String[] args) throws IOException {
        try (InputStream input = new FileInputStream("demo.bin")) {
            byte[] buffer = new byte[8192];
            int len;
            while ((len = input.read(buffer)) != -1) {
                // 只处理 buffer[0, len) 范围内的数据
                System.out.println("read bytes: " + len);
            }
        }
    }
}

注意不要直接处理整个 buffer,因为最后一次读取通常不会填满数组,真实有效长度是 len。

# 3. 写入字节文件

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;

public class WriteBytesDemo {
    public static void main(String[] args) throws IOException {
        byte[] data = "hello io".getBytes(StandardCharsets.UTF_8);

        try (OutputStream output = new FileOutputStream("demo.txt")) {
            output.write(data);
            output.flush();
        }
    }
}

flush() 的作用是把缓冲区中的数据刷新到底层目标。close() 通常会自动调用 flush(),但在需要及时发送数据的场景中仍然会显式调用。

# 4. 文件复制示例

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

public class CopyFileDemo {
    public static void main(String[] args) throws IOException {
        try (InputStream input = new FileInputStream("source.zip");
             OutputStream output = new FileOutputStream("target.zip")) {

            byte[] buffer = new byte[8192];
            int len;
            while ((len = input.read(buffer)) != -1) {
                output.write(buffer, 0, len);
            }
        }
    }
}

这个例子可以复制任意二进制文件,因为它不尝试把字节解释成字符。

# 5. 字节流适合什么场景

适合使用字节流的场景:

  • 图片、音频、视频文件处理。
  • 压缩包、二进制协议处理。
  • HTTP 请求和响应体。
  • Socket 网络通信。
  • 文件上传下载。
  • 加密、摘要、压缩等二进制处理。

不适合只使用字节流直接处理的场景:

  • 逐行读取文本。
  • 按字符处理中文、emoji 等 Unicode 文本。
  • 需要明确字符集的文本解析。

这些场景通常应该使用字符流,或者先用字节流读取后再按指定字符集解码。

# 6. 单字节读取的问题

下面这种写法在小文件中可以工作,但不适合大文件:

int b;
while ((b = input.read()) != -1) {
    output.write(b);
}

原因是每次读取一个字节都可能触发底层 IO 调用,系统调用成本远高于普通内存操作。生产代码中通常使用字节数组作为缓冲区。

# 专家实践清单

场景 推荐写法 不推荐写法
复制普通文件 Files.copy 或缓冲区循环 单字节循环读写
处理大文件 固定大小 byte[] 分块 readAllBytes 一次性读取
写网络响应 按实际读取长度写出 忽略 len 写满缓冲区
资源关闭 try-with-resources 依赖 GC 或手动漏关

字节流读写的关键流程:

创建 InputStream
  │
  ▼
准备 byte[] buffer
  │
  ▼
循环 read,判断 len != -1
  │
  ▼
按 len 处理有效字节
  │
  ▼
关闭资源

# Tips 快问快答

Q:字节流处理的最小单位是什么? A:字节,也就是 byte,适合处理原始二进制数据。

Q:read() 返回 -1 表示什么? A:表示已经读到流末尾,没有更多数据。

Q:为什么写出时要使用 write(buffer, 0, len)? A:最后一次读取可能没有填满缓冲区,只能写真实读取到的字节数。

Q:字节流能不能处理文本? A:可以,但要自己处理编码;文本场景通常用字符流更清晰。

Q:为什么不要单字节循环读取大文件? A:频繁底层 IO 调用成本高,性能会明显变差。

Q:InputStream.available() 能代表文件大小吗? A:不能,它只表示当前可无阻塞读取的估计字节数。

Q:字节流需要 flush 吗? A:输入流不需要;输出流如果有缓冲,关闭前需要刷新或关闭。

Q:生产代码中最常见的字节流错误是什么? A:忘记判断 read 返回长度、忘记关闭资源、一次性读取大文件。

# 7. 总结

字节流是 Java IO 的底层基础。只要数据不是明确的文本,优先使用字节流。写字节流代码时要重点关注三点:循环读取时判断 -1、写入时使用真实长度、通过 try-with-resources 正确关闭资源。

上次更新: 2026/06/25, 14:19:18
IO流体系
字符流

← IO流体系 字符流→

Copyright © 2023-2026 Wray | 鄂ICP备2024050235号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式