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

1. 引言

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

2. 什么是 BufferedInputStream

BufferedInputStream 是 java.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 与 reset :mark(int readlimit) 中的参数表示在标记失效前最多允许读取的字节数,超过该值后 reset 可能失败。
  • 线程安全 :BufferedInputStream 不是线程安全的,多线程环境下需要自行同步。

9. 总结

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

相关推荐
tachibana22 分钟前
Go 语言里的 Context 是什么?
开发语言·后端·golang·go·语言
天桥下的卖艺者7 分钟前
R语言使用ggcube包轻松绘制文章中的各类3D图形
开发语言·3d·r语言
不得了zz29 分钟前
04-Spring AI Alibaba ReactAgent 生产级实践:工具调用、结构化输出、HITL 与上下文管理
java·ai编程·springaialibaba
曲径海盗38 分钟前
【Python 学习第 32 天】字符串基础
python·学习笔记·编程入门
猿究院_夏燊39 分钟前
SpringMVC 到底是怎么干活的?
java·网络·职场和发展
鶴哥只手遮天41 分钟前
Spring Framework 7.0.9 深度剖析-05-配置元数据解析:XML、注解与 JavaConfig
java·后端·开源
NGINX开源社区1 小时前
与我们交流:NGINX Gateway Fabric 与 NGINX Ingress Controller 社区会议
java·服务器·前端
鶴哥只手遮天1 小时前
Spring Framework 7.0.9 深度剖析-16-异步与调度
java·后端·开源
田里的水稻1 小时前
PyTorch_torchvision---数据集下载\TensorBoard可视化
人工智能·pytorch·python·深度学习·机器学习
十年Java程序媛1 小时前
Java 多线程基础与锁机制|进程线程、synchronized、volatile、Lock 面试全梳理
java·spring boot·面试