Java InputStream.read() 为什么返回 int?

15 阅读2分钟

Java InputStream.read() 为什么返回 int?

InputStream.read() 返回 int,因为它既要表示 0..255 的字节值,也要用 -1 表示 EOF;若返回 byte,0xFF 也会被误判为结束,导致数据丢失。

一个返回值,要装下两类信息

Java 的 byte 是 8 位有符号类型,范围是 -128 到 127。但文件或网络流里的一个字节有 256 种取值:0x00 到 0xFF。InputStream.read() 将读到的字节按无符号值返回为 0 到 255,并把范围之外的 -1 留作“已经到末尾”的标记。

因此正确的单字节读取方式是先用 int 判断 EOF:

static void readBytes(Path path) throws IOException {
    try (InputStream in = Files.newInputStream(path)) {
        int value;
        while ((value = in.read()) != -1) {
            // value 一定在 0..255 之间
            consume(value);
        }
    }
}

如果先转成 byte 再判断,问题就出现了:

int value = in.read();
byte b = (byte) value;

当读到 0xFF 时,value 是 255,但转成 byte 后 b 是 -1。如果在转换前没有检查 value == -1,合法数据和 EOF 就会混在一起。需要保留字节数组时,可以在确认不是 EOF 后再转型。

复制文件时,判断的是“本次读到多少”

复制图片、压缩包等二进制内容,通常用缓冲区,而不是每次只读一个字节:

static void copy(Path source, Path target) throws IOException {
    try (InputStream in = Files.newInputStream(source);
         OutputStream out = Files.newOutputStream(target)) {
        byte[] buffer = new byte[8192];
        int count;

        while ((count = in.read(buffer)) != -1) {
            out.write(buffer, 0, count);
        }
    }
}

这里 count 表示本次实际读入的字节数。一次 read(buffer) 不保证填满整个数组,可能只返回一部分;只有返回 -1 才表示后面没有数据。因此不要用“读到的长度等于缓冲区长度”判断是否结束,也不要只调用一次就认为整个文件已经读完。

字节流和字符流的 read() 不要混为一谈

InputStream.read() 面向字节,返回 0..255 或 -1。字符流 Reader.read() 面向字符,返回 0..65535 的字符值或 -1。两者都用 int,是因为返回类型还要容纳 EOF 标记;但表示的数据单位不同。

如果处理的是文本,应使用 Reader 或 InputStreamReader 并明确字符集。不要把每个字节直接强转成 char 来解码 UTF-8 等多字节编码。

**记住这三点:**单字节读取先用 int 判断 -1;缓冲区读取按返回的实际字节数写出;文本解码交给字符流和明确的字符集。

参考:Java SE 21 InputStream.read() 文档、Java SE 21 Reader.read() 文档。