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:
java
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 再判断,问题就出现了:
java
int value = in.read();
byte b = (byte) value;
当读到 0xFF 时,value 是 255,但转成 byte 后 b 是 -1。如果在转换前没有检查 value == -1,合法数据和 EOF 就会混在一起。需要保留字节数组时,可以在确认不是 EOF 后再转型。
复制文件时,判断的是"本次读到多少"
复制图片、压缩包等二进制内容,通常用缓冲区,而不是每次只读一个字节:
java
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() 文档。