前言
今天有一段使用 FileInputStream 读取文件的经典代码。乍一看,这段代码逻辑清晰:打开文件、循环读取、打印内容、关闭流。但如果仔细盯着那个红色的 while 循环和下面的 String 构造方法看,就会发现里面藏着 Java IO 编程中几个非常重要的细节。
今天这篇随笔,就讨论为什么我们读取文件时要用 byte[] 数组,以及那个神秘的变量 a 到底起了什么作用。

1.为什么要用 byte[] 数组(缓冲区)?
很多初学者会问:“为什么不直接一个字节一个字节地读?”
这就涉及到了 IO 效率 的问题。硬盘的读写速度远慢于内存和 CPU。如果每次只读 1 个字节,就要进行一次磁盘 IO 操作,这就像去超市买东西,买一颗糖跑一趟,买一瓶水又跑一趟,效率极低。
代码中的 byte[] tom = new byte[18] 就是所谓的 “缓冲区”。它的作用是一次性从硬盘搬运 18 个字节到内存中。虽然 18 个字节在实际开发中显得很小(通常我们会用 1024 或 8192),但在教学演示中,它能很好地帮助我们观察数据是如何分批到达的。
- 核心难点解析:变量 a 的重要性
while((a=in.read(tom, 0, 18)) != -1)
这里的 in.read(tom, 0, 18) 做了三件事:
尝试读取最多 18 个字节的数据。
将读到的数据存入 tom 数组。
返回实际读取的字节数,赋值给变量 a 。
关键点来了: 当文件读到末尾时,剩余的内容可能不足 18 个字节。比如文件还剩 5 个字节,那么 read 方法只会往数组里填 5 个字节,并返回 5 。此时,数组的第 6 到第 18 个位置,保留的还是上一次读取的旧数据!
这就是为什么下一行代码必须写成:
String s = new String(tom, 0, a);
而不是简单的 new String(tom) 。
new String(tom) :会把数组里所有的 18 个字节都转成字符串,导致最后一段内容后面跟着一堆乱码或重复的旧数据。
new String(tom, 0, a) :明确告诉构造函数,“我只需要数组里从 0 开始的前 a 个字节”,从而精准地还原了文件内容。
- 总结与思考
这段简短的代码其实涵盖了 IO 流的三个基本功:
File 对象的使用:通过父路径和子文件名构建文件对象。
缓冲读取:利用数组减少 IO 次数。
边界处理:利用返回值 a 处理最后一次读取可能不满缓冲区的情况。
