available()返回的是当前可无阻塞读取的字节数,而非剩余总字节;其值不被jdk规范保证,受缓冲、文件系统及包装流影响,可能为0;可靠获取剩余字节数应使用filechannel.position()与size()计算。

available() 返回的不是“剩余总字节”,而是“当前可无阻塞读取的字节数”
这是最常被误解的一点:available() 从不保证返回整个文件剩余字节数。它只反映底层输入源当前缓冲区中已就绪、能立刻读出的字节数——对 FileInputStream 来说,多数情况下等于文件未读部分的长度(因为文件是本地、可随机访问的),但这个行为**不被 JDK 规范保证**,也不适用于所有子类(比如网络流或加密流)。
- 如果你刚 new 出
FileInputStream,且文件没被读过,available()通常返回文件总大小(但别依赖它做长度判断,用file.length()更可靠) - 如果已经调用过几次
read(),available()可能返回一个比实际剩余字节少的值(尤其在某些 JVM 实现或特殊文件系统上) - 它不能替代
mark()/reset()或skip(),也不能用于预分配缓冲区大小——因为后续 read 仍可能阻塞或返回更少字节
为什么 FileInputStream.available() 在某些场景下返回 0?
常见于以下情况:
- 文件被其他进程以独占方式打开(如 Windows 上被记事本锁定),JVM 可能无法准确探测长度
- 使用了包装流(例如
BufferedInputStream),此时调用的是包装类的available(),它只反映内部缓冲区剩余,而非原始文件 - 文件路径指向管道、设备文件或特殊文件系统(如 /proc/xxx),其长度不可预知
- JDK 版本差异:较老版本(如 Java 6)在某些平台对大文件的
available()处理不一致
替代方案:安全获取剩余字节数的正确做法
若你真正需要的是“从当前位置到文件末尾还有多少字节”,不要靠 available() 推算,而应结合 getChannel().position() 和 getChannel().size():
FileInputStream fis = new FileInputStream("data.bin");
FileChannel channel = fis.getChannel();
long pos = channel.position();
long size = channel.size();
long remaining = size - pos; // 这才是可靠的剩余字节数
- 该方法要求文件通道处于可读状态,且文件必须支持随机访问(普通磁盘文件都满足)
- 注意
channel.position()会随每次read()自动更新,无需手动维护 - 相比
available(),它不受缓冲策略影响,结果确定、跨平台一致
available() 的唯一合理用途:避免单字节 read() 的频繁系统调用
它最适合用在“试探性预读”场景,比如实现一个轻量级的 peek 操作:
if (fis.available() > 0) {
int b = fis.read(); // 此时基本不会阻塞
}
- 不要用它做循环条件(如
while (fis.available() > 0)),这会导致逻辑错误或死循环 - 不要用它判断流是否结束——正确方式永远是检查
read()返回值是否为-1 - 在高吞吐读取中,它对性能几乎无帮助;真正有效的是用固定大小缓冲区配合
read(byte[])
关键提醒:available() 是个“尽力而为”的提示方法,不是契约。任何依赖它返回精确剩余字节数的逻辑,都在埋隐患。











