“你好,阿米戈!今天,我们将再次探讨 InputStreamOutputStream 的工作原理。最初的解释实际上有点过于简化了。这二者不是接口。它们是抽象类,并且甚至有几个已实现的方法。现在我们来看一下它们所包含的方法:”

InputStream 方法 方法的用途
int read(byte[] buff);
此方法立即将一个字节块读入缓冲区字节数组),直至缓冲区已满或源中不再有可供读取的字节。
该方法返回实际读取的字节数(可以小于数组的长度)
int read();
此方法读取一个字节并返回该字节。结果被拓展为 int 以便于查看。如果不再有可供读取的字节,则该方法返回 -1。
int available();
此方法返回未读(可用)字节数。
void close();
此方法“关闭”流。使用完流之后,可以调用此方法。
然后,对象执行关闭文件所需的整理操作,等等。
此时,你无法再从流中读取任何数据。

“因此,我们不仅可以读取单个字节,还可以读取整个块?”

“完全正确。”

“我们也可以写入整个块吗?”

“是的,我们来看一下:”

OutputStream 方法 方法的用途
void write(int c);
此方法写入一个字节。int 类型窄化为一个字节。多余部分被直接丢弃。
void write(byte[] buff);
此方法写入一个字节块。
void write(byte[] buff, int from, int count);
此方法写入字节块的一部分。在字节数组可能尚未完全填充的情况下使用。
void flush();
如果流在内部存储尚未写入的任何数据,则此方法强制写入数据。
void close();
此方法“关闭”流。使用完流之后,可以调用此方法。
然后,对象执行关闭文件所需的整理操作,等等。你无法再将数据写入流中,并自动调用 flush。

“如果我们一次读取整个块而不是单个字节,那么文件复制代码会是什么样?”

“嗯。就像下面这样:”

复制磁盘上的文件
public static void main(String[] args) throws Exception
{
 //Create a stream to read bytes from a file
 FileInputStream inputStream = new FileInputStream("c:/data.txt");
 //Create a stream to write bytes to a file
 FileOutputStream outputStream = new FileOutputStream("c:/result.txt");

  byte[] buffer = new byte[1000];
 while (inputStream.available() > 0) //as long as there are unread bytes
 {
  //Read the next block of bytes into buffer, and store the actual number of bytes read in count.
  int count = inputStream.read(buffer);
  outputStream.write(buffer, 0, count); //Write a block (part of a block) to the second stream
 }

 inputStream.close(); //Close both streams. We don't need them any more.
 outputStream.close();
}

“我对缓冲区完全了解,但是这个 count 变量是什么?”

“当我们从文件中读取最新的数据块时,举例来说,我们可能会得到 328 个字节,而不是 1000 个字节。因此在写入数据时,我们需要指出我们并不写入整个块,而只写入前 328 个字节。”

当我们读取最后一个块时,read 方法将返回实际读取的字节数。每次读取一个块时,我们会获取 1000 个字节,而在读取最后一个块时,我们获取 328 个字节。

因此在写入块时,我们指出不要写入缓冲区中的所有字节,而只写入 328(即存储在 count 变量中的值)个字节。

“现在我全明白了。谢谢你,艾莉。”