Java BufferedInputStream 详解:原理、用法与实战

1. 引言

在 Java 的 I/O 体系中,BufferedInputStream 是一个非常重要的缓冲输入流。它通过在内存中维护一个缓冲区,减少底层系统调用的次数,从而显著提升文件读取和网络数据读取的性能。本文将从原理、构造方法、常用方法到实战示例,全面讲解 BufferedInputStream 的使用。

2. 什么是 BufferedInputStream

BufferedInputStreamjava.io 包中的一个类,它继承自 FilterInputStream,本质上是为另一个输入流添加缓冲功能的包装流。当程序频繁读取单个字节或少量字节时,直接操作底层流会产生大量系统调用,性能较差;而 BufferedInputStream 会一次性从底层流读取较多数据到内存缓冲区,后续读取直接从缓冲区返回,从而大幅提升效率。

3. 类继承关系与构造方法

先来看 BufferedInputStream 在 Java 类体系中的位置:

java 复制代码
java.lang.Object
  └── java.io.InputStream
        └── java.io.FilterInputStream
              └── java.io.BufferedInputStream

BufferedInputStream 提供了两个构造方法:

  • BufferedInputStream(InputStream in):创建一个默认缓冲区大小(通常为 8192 字节)的缓冲输入流。
  • BufferedInputStream(InputStream in, int size):指定缓冲区大小创建缓冲输入流。

4. 常用方法

BufferedInputStream 继承并重写了 InputStream 的核心方法,常用方法如下:

方法 说明
int read() 读取一个字节,返回 0 到 255 的整数,读到末尾返回 -1。
int read(byte[] b) 读取若干字节到数组中,返回实际读取的字节数。
int read(byte[] b, int off, int len) 从指定偏移位置开始读取 len 个字节到数组中。
long skip(long n) 跳过并丢弃 n 个字节。
int available() 返回可以无阻塞读取的估计字节数。
void close() 关闭流并释放相关资源。
void mark(int readlimit) 标记当前读取位置。
void reset() 将读取位置重置到最近一次 mark 的位置。
boolean markSupported() 判断该流是否支持 mark 和 reset 操作,BufferedInputStream 返回 true。

5. 缓冲原理

BufferedInputStream 的核心是一个内部字节数组 buf,配合 pos(当前位置)、count(缓冲区有效字节数)和 markpos(标记位置)等字段实现缓冲逻辑。其工作流程大致如下:

  1. 当调用 read() 时,先检查缓冲区中是否还有未读数据。
  2. 如果缓冲区有数据,直接从 buf[pos++] 返回。
  3. 如果缓冲区数据已读完,则调用底层输入流的 read() 方法,一次性填充缓冲区。
  4. 重复以上过程,直到底层流返回 -1 表示读取结束。

通过这种方式,即使程序逐字节读取,底层流也只在缓冲区耗尽时才被真正调用,从而减少了系统调用次数。

6. 实战示例

下面通过一个完整的示例演示如何使用 BufferedInputStream 读取文件内容:

java 复制代码
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;

public class BufferedInputStreamDemo {
    public static void main(String[] args) {
        try (BufferedInputStream bis = new BufferedInputStream(
                new FileInputStream("example.txt"))) {
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = bis.read(buffer)) != -1) {
                System.out.print(new String(buffer, 0, bytesRead));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

上述代码使用 try-with-resources 语法自动关闭流,通过 1024 字节的数组批量读取文件内容,既保证了性能,又避免了内存浪费。

7. 性能对比

为了直观感受缓冲带来的性能提升,可以将普通 FileInputStream 逐字节读取与 BufferedInputStream 逐字节读取进行对比。在读取大文件时,BufferedInputStream 通常比直接逐字节读取快数倍甚至数十倍,因为后者每次 read() 都会触发一次系统调用。

8. 注意事项

  • 缓冲区大小:默认 8192 字节通常已足够,频繁读取大文件时可适当调大缓冲区,但不宜过大,以免浪费内存。
  • 资源关闭 :关闭 BufferedInputStream 时,其内部的底层流也会被自动关闭,无需重复关闭。
  • mark 与 resetmark(int readlimit) 中的参数表示在标记失效前最多允许读取的字节数,超过该值后 reset 可能失败。
  • 线程安全BufferedInputStream 不是线程安全的,多线程环境下需要自行同步。

9. 总结

BufferedInputStream 是 Java I/O 中提升读取性能的利器,它通过内存缓冲减少系统调用,适用于文件读取、网络流处理等场景。掌握其原理和常用方法,能够帮助开发者写出更高效的 I/O 代码。在实际开发中,建议结合 try-with-resources 使用,确保资源正确释放。

相关推荐
my05921 小时前
跳出单一信息工具局限:奥米豆构建认知与心智并行的学习范式
大数据·人工智能·python·学习
weixin199701080161 小时前
[特殊字符]《二手ERP × 闲鱼消息驱动架构:正向+逆向交易消息如何驱动WMS出库与回传》(附Python源码)
开发语言·python·架构
galaxy_strive1 小时前
Qt C++插件化编写项目(1)
开发语言·c++·qt
~木雨1 小时前
Java 线程池七问七答:参数、执行流程、拒绝策略到 ThreadLocal 内存泄漏,面试必背
java·面试·线程池·threadlocal·threadpool·executor
见叶之秋1 小时前
【C++】C++ 核心进阶(一):泛型编程基石 —— 模板初阶与 STL 体系开篇
开发语言·c++
Escalating_xu1 小时前
【Makefile 进阶】从自动发现源文件、模式规则到目录分离与多模块递归构建
linux·开发语言
遇见小修修1 小时前
打印机连不上电脑无法打印
大数据·运维·python·电脑
艾莉丝努力练剑1 小时前
【AI大模型接入SDK】Gemini模型接入知识体系
java·开发语言·网络·人工智能·网络协议·学习·http
AC赳赳老秦1 小时前
电力能源公开数据采集实操:用 OpenClaw 合规抓取电网电价与发电量数据,生成区域能源供需分析报告
大数据·数据库·人工智能·python·php·deepseek·openclaw