ZetCode

Java InputStream 类

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

java.io.InputStream 类是一个抽象超类,表示字节的输入流。它是 Java 中所有字节输入流的基础。具体的子类实现了特定的输入源,例如文件、网络连接或内存缓冲区。

InputStream 提供了从各种源读取字节的基本方法。它支持基本的读取操作、流标记、跳过和资源管理。所有方法都会在发生 I/O 错误时抛出 IOException

InputStream 类概述

InputStream 是一个抽象类,它定义了读取字节的核心功能。主要方法包括各种读取操作、流控制和资源管理。子类必须至少实现基本的 read 方法。

public abstract class InputStream implements Closeable {
    public abstract int read() throws IOException;
    public int read(byte[] b) throws IOException;
    public int read(byte[] b, int off, int len) throws IOException;
    public long skip(long n) throws IOException;
    public int available() throws IOException;
    public void close() throws IOException;
    public synchronized void mark(int readlimit);
    public synchronized void reset() throws IOException;
    public boolean markSupported();
}

上面的代码展示了 InputStream 提供的关键方法。这些方法构成了 Java 中所有字节输入操作的基础。抽象的 read 方法必须由具体的子类实现。

读取单个字节

最基本的操作是从流中读取单个字节。read 方法将字节作为 int (0-255) 返回,或者在流的末尾返回 -1。此方法会阻塞,直到输入可用或到达流的末尾。

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

public class Main {

    public static void main(String[] args) {
        byte[] data = {65, 66, 67, 68, 69}; // ABCDE
        try (InputStream is = new ByteArrayInputStream(data)) {
            
            int byteRead;
            while ((byteRead = is.read()) != -1) {
                System.out.println("Read byte: " + byteRead + 
                    " (" + (char) byteRead + ")");
            }
            
            System.out.println("End of stream reached");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例演示了从 ByteArrayInputStream 读取字节。try-with-resources 确保正确关闭流。每个字节都以数值和字符的形式打印。循环会一直持续到 read 返回 -1,表示流的末尾。

将字节读入数组

为了获得更好的性能,一次性将多个字节读入字节数组。这减少了方法调用并提高了效率。read 方法返回实际读取的字节数,或者在流的末尾返回 -1。

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

public class Main {

    public static void main(String[] args) {
        String text = "Hello, InputStream!";
        byte[] data = text.getBytes();
        
        try (InputStream is = new ByteArrayInputStream(data)) {
            byte[] buffer = new byte[10];
            int bytesRead;
            
            while ((bytesRead = is.read(buffer)) != -1) {
                System.out.println("Read " + bytesRead + " bytes: " + 
                    new String(buffer, 0, bytesRead));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例展示了批量读取到 10 字节的缓冲区中。String 构造函数仅将实际读取的字节转换为文本。循环会一直持续到 read 返回 -1。每次迭代最多处理 10 个字节。

跳过流中的字节

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

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

public class Main {

    public static void main(String[] args) {
        byte[] data = new byte[100];
        for (int i = 0; i < data.length; i++) {
            data[i] = (byte) i;
        }
        
        try (InputStream is = new ByteArrayInputStream(data)) {
            System.out.println("Available before skip: " + is.available());
            
            long skipped = is.skip(50);
            System.out.println("Skipped " + skipped + " bytes");
            
            System.out.println("Next byte: " + is.read());
            System.out.println("Available after skip: " + is.available());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例创建了一个 100 字节的流,并跳过前 50 个字节。available 方法显示跳过之前和之后剩余的字节数。跳过后的读取显示跳过部分后的第一个字节 (50)。

Mark 和 Reset 功能

某些 InputStream 实现支持 mark/reset 操作。这允许在提前读取后返回到标记的位置。mark 方法指定在标记失效之前可以读取多少字节。

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

public class Main {

    public static void main(String[] args) {
        String text = "Markable InputStream Example";
        try (InputStream is = new ByteArrayInputStream(text.getBytes())) {
            
            if (is.markSupported()) {
                System.out.println("Mark supported");
                
                // Read and mark after 5 bytes
                byte[] buffer = new byte[5];
                is.read(buffer);
                System.out.println("First 5 bytes: " + new String(buffer));
                
                is.mark(10); // Mark with readlimit of 10 bytes
                
                // Read next 5 bytes
                is.read(buffer);
                System.out.println("Next 5 bytes: " + new String(buffer));
                
                // Reset to mark position
                is.reset();
                
                // Read again from mark
                is.read(buffer);
                System.out.println("After reset: " + new String(buffer));
            } else {
                System.out.println("Mark not supported");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例演示了 ByteArrayInputStream 的 mark/reset 功能。在读取 5 个字节后,我们标记该位置。读取超过 readlimit (10 字节) 会使标记失效。reset 返回到标记的位置。

从文件读取

FileInputStream 是一个常见的 InputStream 实现,用于读取文件。它直接从文件系统中的文件读取字节。始终关闭文件流以释放系统资源。

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

public class Main {

    public static void main(String[] args) {
        try (InputStream is = new FileInputStream("example.txt")) {
            System.out.println("Available bytes: " + is.available());
            
            byte[] buffer = new byte[1024];
            int bytesRead;
            StringBuilder content = new StringBuilder();
            
            while ((bytesRead = is.read(buffer)) != -1) {
                content.append(new String(buffer, 0, bytesRead));
            }
            
            System.out.println("File content:\n" + content.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例使用 FileInputStream 读取文件。available 方法给出初始文件大小。我们将数据块读入缓冲区并在 StringBuilder 中构建内容。try-with-resources 确保正确关闭文件句柄。

可用字节和流状态

available 方法估计可以读取而不会阻塞的字节数。对于文件,这通常意味着剩余字节。该方法对于检查流状态和进度很有用。

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

public class Main {

    public static void main(String[] args) {
        byte[] data = new byte[1000];
        try (InputStream is = new ByteArrayInputStream(data)) {
            
            System.out.println("Initial available: " + is.available());
            
            byte[] buffer = new byte[100];
            is.read(buffer);
            System.out.println("After reading 100 bytes: " + is.available());
            
            is.skip(300);
            System.out.println("After skipping 300 bytes: " + is.available());
            
            is.read(new byte[500]);
            System.out.println("After reading 500 bytes: " + is.available());
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例在各种流操作期间跟踪可用字节。初始计数显示完整的流大小。每个读取或跳过操作都会减少可用计数。该方法有助于监控读取进度。

来源

Java InputStream 类文档

在本文中,我们介绍了 Java InputStream 类的基本方法和特性。理解这些概念对于在 Java 应用程序中使用面向字节的输入操作至关重要。

作者

我叫 Jan Bodnar,是一位经验丰富的程序员。我于 2007 年开始撰写编程文章,至今已撰写了 1,400 多篇文章和八本电子书。凭借八年多的教学经验,我致力于分享我的知识,帮助他人掌握编程概念。

列出所有Java教程