ZetCode

Java BufferedInputStream 类

最后修改时间:2025 年 4 月 16 日

java.io.BufferedInputStream 类为另一个输入流添加缓冲功能。它通过减少本地 I/O 调用的数量来提高性能。数据从底层流以大块的形式读取到缓冲区中。

BufferedInputStream 封装另一个 InputStream,并提供缓冲读取功能。默认缓冲区大小为 8192 字节 (8KB),但可以指定自定义大小。此类对于并发访问是线程安全的。

BufferedInputStream 类概述

BufferedInputStream 扩展 FilterInputStream 并提供缓冲输入操作。主要方法包括读取操作、mark/reset 功能和流跳过。当执行读取操作时,缓冲区会被填满。

public class BufferedInputStream extends FilterInputStream {
    public BufferedInputStream(InputStream in);
    public BufferedInputStream(InputStream in, int size);
    public synchronized int read();
    public synchronized int read(byte[] b, int off, int len);
    public synchronized long skip(long n);
    public synchronized int available();
    public synchronized void mark(int readlimit);
    public synchronized void reset();
    public boolean markSupported();
    public synchronized void close();
}

上面的代码展示了 BufferedInputStream 提供的关键方法。这些方法允许通过缓冲进行高效的数据读取。如果底层流支持它们,则该类支持 mark 和 reset 操作。

创建 BufferedInputStream

BufferedInputStream 通过将其封装在另一个 InputStream 中创建。您可以指定缓冲区大小或使用默认值。缓冲区大小会影响 I/O 性能 - 较大的缓冲区减少本地调用,但使用更多内存。

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

public class Main {

    public static void main(String[] args) {
        try {
            // Create with default buffer size
            InputStream fileStream = new FileInputStream("data.txt");
            BufferedInputStream bufferedStream1 = new BufferedInputStream(fileStream);
            
            // Create with custom buffer size (16KB)
            InputStream fileStream2 = new FileInputStream("data.txt");
            BufferedInputStream bufferedStream2 = 
                new BufferedInputStream(fileStream2, 16384);
            
            System.out.println("Default buffer stream created");
            System.out.println("Custom buffer (16KB) stream created");
            
            bufferedStream1.close();
            bufferedStream2.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例演示了创建 BufferedInputStream 的不同方法。第一个使用默认缓冲区大小,第二个指定 16KB。完成后务必关闭流以释放资源。关闭 BufferedInputStream 时,底层 FileInputStream 会自动关闭。

使用 BufferedInputStream 读取数据

BufferedInputStream 提供了几种用于读取数据的方法。最简单的方法是读取单个字节。更有效的方法是将多个字节读入数组。所有读取操作都是同步的,以确保线程安全。

Main.java
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;

public class Main {

    public static void main(String[] args) {
        try (BufferedInputStream bis = 
                new BufferedInputStream(new FileInputStream("data.txt"))) {
            
            // Read single byte
            int byteData;
            while ((byteData = bis.read()) != -1) {
                System.out.print((char) byteData);
            }
            
            System.out.println("\n\nReading complete");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例展示了如何使用 BufferedInputStream 逐字节读取文件。try-with-resources 语句确保了流的正确关闭。read 方法在流的末尾返回 -1。每次调用 read 可能会从缓冲区而不是底层流中获取数据。

将字节读入数组

为了获得更好的性能,一次将多个字节读入一个字节数组。这减少了方法调用和本地 I/O 操作。read 方法返回实际读取的字节数。

Main.java
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;

public class Main {

    public static void main(String[] args) {
        try (BufferedInputStream bis = 
                new BufferedInputStream(new FileInputStream("largefile.dat"))) {
            
            byte[] buffer = new byte[1024];
            int bytesRead;
            
            while ((bytesRead = bis.read(buffer)) != -1) {
                System.out.println("Read " + bytesRead + " bytes");
                // Process the buffer data here
            }
            
            System.out.println("File reading complete");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例演示了批量读取到字节数组中。可以根据性能需求调整缓冲区大小 (1024)。bytesRead 值指示实际读取到数组中的字节数。这可能小于文件末尾的数组长度。

Mark 和 Reset 功能

BufferedInputStream 支持 mark 和 reset 操作以重新读取数据。mark 方法标记当前位置,reset 返回到该位置。readlimit 参数指定在 mark 变为无效之前可以读取的字节数。

Main.java
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;

public class Main {

    public static void main(String[] args) {
        String data = "BufferedInputStream mark/reset example";
        byte[] bytes = data.getBytes();
        
        try (BufferedInputStream bis = 
                new BufferedInputStream(new ByteArrayInputStream(bytes))) {
            
            // Read first 10 bytes
            byte[] firstPart = new byte[10];
            bis.read(firstPart);
            System.out.println("First part: " + new String(firstPart));
            
            // Mark current position
            bis.mark(20); // Allow 20 bytes to be read before mark invalid
            
            // Read next 10 bytes
            byte[] secondPart = new byte[10];
            bis.read(secondPart);
            System.out.println("Second part: " + new String(secondPart));
            
            // Reset back to mark
            bis.reset();
            
            // Read again from marked position
            bis.read(secondPart);
            System.out.println("Second part after reset: " + new String(secondPart));
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例演示了 mark 和 reset 功能。在读取前 10 个字节后设置 mark。在读取接下来的 10 个字节后,reset 返回到标记位置。readlimit 为 20 意味着在标记后读取超过 20 个字节将使标记无效。

跳过流中的字节

skip 方法允许跳过流中指定数量的字节。这比读取和丢弃数据更有效。实际跳过的字节数可能小于请求的字节数。

Main.java
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;

public class Main {

    public static void main(String[] args) {
        String data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        byte[] bytes = data.getBytes();
        
        try (BufferedInputStream bis = 
                new BufferedInputStream(new ByteArrayInputStream(bytes))) {
            
            System.out.println("Starting position: " + bis.available());
            
            // Skip first 10 bytes
            long skipped = bis.skip(10);
            System.out.println("Skipped " + skipped + " bytes");
            
            // Read next byte
            int nextByte = bis.read();
            System.out.println("Next byte: " + (char) nextByte);
            
            // Skip beyond end of stream
            skipped = bis.skip(20);
            System.out.println("Skipped " + skipped + " bytes (end approached)");
            
            System.out.println("Remaining bytes: " + bis.available());
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例展示了如何在流中跳过字节。第一个 skip 跳过前 10 个字母。第二个 skip 尝试跳过 20 个字节,但只跳过剩余的字节。available 方法显示无需阻塞即可读取的字节数。

可用字节和性能

available 方法返回一个估计值,表示无需阻塞即可读取的字节数。BufferedInputStream 的实现同时考虑了缓冲区和底层流。这对于检查流状态很有用。

Main.java
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;

public class Main {

    public static void main(String[] args) {
        try (BufferedInputStream bis = 
                new BufferedInputStream(new FileInputStream("data.bin"))) {
            
            System.out.println("Initially available: " + bis.available() + " bytes");
            
            // Read some data
            byte[] buffer = new byte[100];
            bis.read(buffer);
            
            System.out.println("After reading 100 bytes, available: " 
                + bis.available() + " bytes");
            
            // Fill buffer by reading more
            while (bis.available() > 0) {
                bis.read(buffer);
                System.out.println("Read 100 bytes, remaining: " 
                    + bis.available());
            }
            
            System.out.println("Stream fully read");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例演示了使用 available 来监视流的读取进度。该方法返回缓冲区和底层流中剩余的总字节数。请注意,available 只是一个估计值,不应用于确定确切的剩余数据大小。

来源

Java BufferedInputStream 类文档

在本文中,我们介绍了 Java BufferedInputStream 类的基本方法和特性。理解这些概念对于在 Java 应用程序中处理高效的 I/O 操作至关重要。

作者

我叫 Jan Bodnar,是一位经验丰富的程序员,在这一领域拥有多年的经验。我从 2007 年开始撰写编程文章,至今已撰写了 1400 多篇文章和 8 本电子书。凭借 8 年以上的教学经验,我致力于分享我的知识,并帮助他人掌握编程概念。

列出所有Java教程