Java InputStream

时间:2020-01-09 10:36:04  来源:igfitidea点击:

Java InputStream类java.io.InputStream表示字节的有序流。换句话说,我们可以从JavaInputStream中读取数据,作为字节的有序序列。从文件读取数据或者通过网络接收数据时,此函数很有用。

InputStream子类

JavaInputStream类是Java IO API中所有输入流的基类(超类)。 InputStream的每个子类通常都有非常特定的用途,但可以用作InputStream。 InputStream子类是:

  • ByteArrayInputStream
  • FileInputStream
  • PipedInputStream
  • BufferedInputStream
  • FilterInputStream
  • PushbackInputStream
  • 数据输入流
  • ObjectInputStream
  • SequenceInputStream

InputStreams和源

Java" InputStream"通常连接到某些数据源,例如文件,网络连接,管道等。这在Java IO概述文本中也有更详细的说明。

Java InputStream示例

Java" InputStream"用于读取基于字节的数据,一次读取一个字节。这是一个Java InputStream示例,该示例读取文件中的所有字节:

InputStream inputstream = new FileInputStream("c:\data\input-text.txt");

int data = inputstream.read();
while(data != -1) {
  //do something with data...
  doSomethingWithData(data);

  data = inputstream.read();
}
inputstream.close();

这个例子创建了一个新的FileInputStream实例。 FileInputStream是InputStream的子类,因此可以安全地将FileInputStream的实例分配给InputStream变量(inputstream变量)。

read()

InputStream的read()方法返回一个int,其中包含读取的字节的字节值。这是一个InputStreamread()示例:

int data = inputstream.read();

要读取Java InputStream中的所有字节,我们必须继续读取直到返回值-1为止。这个值意味着没有更多的字节可以从InputStream中读取。这是一个从Java InputStream读取所有字节的示例:

int data = inputStream.read();
while(data != -1) {
    // do something with data variable

    data = inputStream.read(); // read next byte
}

InputStream的子类可能具有替代的read()方法。例如,DataInputStream允许我们读取Java原语,例如int,long,float,double,boolean等,以及其相应的方法readBoolean()readDouble()等。

流结束

如果read()方法返回-1,则说明已到达流的末尾,这意味着InputStream中不再有要读取的数据。也就是说,-1作为int值,而不是-1作为byte或者short值。这里有区别!

当到达流的末尾时,我们可以关闭InputStream

read(byte[])

InputStream类还包含两个read方法,可以将InputStream源中的数据读取到byte数组中。这些方法是:

  • int read(byte [])
  • int read(byte [],int offset,int length)

read(byte [])方法将尝试读取作为参数给定的byte数组中尽可能多的字节,因为该数组有足够的空间。 " read(byte [])"方法返回一个" int",告诉我们实际读取了多少个字节。如果从InputStream读取的字节少于字节数组的空间,则字节数组的其余部分将包含与读取开始之前相同的数据。记住检查返回的int以查看实际将多少字节读入byte数组。

read(byte [],int offset,int length)方法也将字节读取到" byte"数组中,但从" offset"字节开始读取到数组中,并从中读取最大" length"字节到数组中。那个位置。同样," read(byte [],int偏移量,int长度)"方法返回一个" int",告诉我们实际上已将多少字节读取到数组中,因此请记住在处理读取的字节之前检查此值。

对于这两种方法,如果都已到达流的末尾,则该方法返回-1作为读取的字节数。

这是一个示例,说明如何使用InputStream的read(byte [])方法:

InputStream inputstream = new FileInputStream("c:\data\input-text.txt");

byte[] data      = new byte[1024];
int    bytesRead = inputstream.read(data);

while(bytesRead != -1) {
  doSomethingWithData(data, bytesRead);

  bytesRead = inputstream.read(data);
}
inputstream.close();

首先,此示例创建一个"字节"数组。然后,它创建一个名为" bytesRead"的" int"变量,以保存每次" read(byte [])"调用读取的字节数,并立即为" bytesRead"分配从第一个" read(byte [])"调用返回的值。

在while循环内部,调用doSomethingWithData()方法,并传递data字节数组以及作为参数读取到数组中的字节数。在" while"循环的结尾,数据再次被读入" byte"数组。

无需花费太多的想象力就能弄清楚如何使用read(byte [],int offset,int length)方法而不是read(byte [])。我们只需将read(byte [])调用替换为read(byte [],int偏移量,int长度)调用即可。

读的性能

一次读取一个字节数组比一次从Java InputStream读取单个字节要快。通过读取字节数组而不是一次读取单个字节,该差异很容易成为性能提高的10倍或者更多倍。

获得的确切速度取决于我们读取的字节数组的大小以及运行代码的计算机的OS,硬件等。在决定之前,我们应该研究目标系统的硬盘缓冲区大小等。但是,如果缓冲区大小为8KB或者更高,则可以实现很好的加速。但是,一旦字节数组超出了底层操作系统和硬件的容量,我们将无法从更大的字节数组中获得更大的加速。

我们可能必须尝试不同的字节数组大小并测量读取性能,以找到最佳的字节数组大小。

通过BufferedInputStream进行透明缓冲

我们可以使用Java BufferedInputStream从InputStream添加透明的自动读取和缓冲字节数组。 BufferedInputStream从底层的InputStream读取字节块到字节数组。然后,我们可以从" BufferedInputStream"中逐个读取字节,并且仍然可以通过读取字节数组而不是一次读取一个字节来获得很多加速。这是将Java InputStream封装在BufferedInputStream中的示例:

InputStream input = new BufferedInputStream(
                      new FileInputStream("c:\data\input-file.txt"),
                        1024 * 1024        /* buffer size */
    );

注意," BufferedInputStream"是" InputStream"的子类,可以在可以使用" InputStream"的任何地方使用。

mark()和reset()

InputStream类有两个方法,分别是mark()和reset(),它们可能支持也可能不支持InputStream的子类。

如果InputStream子类支持mark()和reset()方法,则该子类应覆盖markSupported()以返回true。如果" markSupported()"方法返回" false",则" mark()"和" reset()"为。

" mark()"在" InputStream"中在内部设置一个标记,该标记标记流中到目前为止已读取数据的点。然后,使用InputStream的代码可以继续从中读取数据。如果使用InputStream的代码想回到流中设置标记的位置,则代码在InputStream上调用reset()。然后," InputStream""倒带"并回到标记处,并再次从该点开始返回(读取)数据。当然,这将导致某些数据从InputStream返回多次。

实现解析器时通常使用方法mark()和reset()方法。有时,解析器可能需要在InputStream中提前读取,如果解析器未找到预期的内容,则可能需要后退并尝试将读取的数据与其他内容进行匹配。

关闭InputStream

完成Java InputStream后,必须将其关闭。我们可以通过调用InputStream close()方法来关闭InputStream。这是一个打开InputStream,从中读取所有数据,然后关闭它的示例:

InputStream inputstream = new FileInputStream("c:\data\input-text.txt");

int data = inputstream.read();
while(data != -1) {
  data = inputstream.read();
}
inputstream.close();

注意while循环如何继续,直到从InputStream的read()方法中读取-1值为止。之后,while循环退出,并调用InputStreamclose()方法。

上面的代码不是100%健壮的。如果从InputStream读取数据时引发了异常,则永远不会调用close()方法。为了使代码更健壮,我们将必须使用Java try-with-resources构造。我在Java IO异常处理的教程中也解释了使用Java IO类的正确异常处理。

这是一个使用try-with-resources构造关闭JavaInputStream的示例:

try( InputStream inputstream = new FileInputStream("file.txt") ) {

    int data = inputstream.read();
    while(data != -1){
        data = inputstream.read();
    }
}

注意,如何在关键字try后面的括号内声明InputStream。这向Java发出信号,该" InputStream"将由try-with-resources构造进行管理。

一旦执行线程退出" try"块," inputstream"变量就会关闭。如果从try块内部抛出异常,则捕获该异常,关闭InputStream,然后重新抛出该异常。因此,当在try-with-resources块内使用时,可以保证InputStream处于关闭状态。

将InputStream转换为Reader

Java" InputStream"是基于字节的数据流。如我们所知,Java IO API还具有一个基于字符的输入流集,称为"读取器"。我们可以使用Java InputStreamReader将Java InputStream转换为Java Reader。我们可以通过单击前一句中的链接来了解有关如何使用InputStreamReader的更多信息,但这是将InputStream转换为InputStreamReader的快速示例:

InputStream inputStream       = new FileInputStream("c:\data\input.txt");
Reader      inputStreamReader = new InputStreamReader(inputStream);