InputStream
# InputStream
InputStream
是 Java 标准库提供的最基本的输入流,它是一个抽象类,是所有输入流的超类。
类的定义如下
public abstract class InputStream implements Closeable
# read 方法介绍
InputStream
最重要的方法就是 int read()
,签名如下:
public abstract int read() throws IOException;
这个方法会读取输入流的下一个字节,并返回字节表示的 int
值(0~255)。如果已读到末尾,返回 -1
表示不能继续读取了。
在计算机中,类似文件、网络端口这些资源,都是由操作系统统一管理的。应用程序在运行的过程中,如果打开了一个文件进行读写,完成后要及时地关闭,以便让操作系统把资源释放掉,否则,应用程序占用的资源会越来越多,不但白白占用内存,还会影响其他应用程序的运行。
InputStream
和 OutputStream
都是通过 close()
方法来关闭流。关闭流就会释放对应的底层资源。
为了避免程序异常导致没有关闭流,我们可以用 try ... finally
。
# read 方法实践
我们来实践下,逐个读取一个文件的字节,这里我们用 FileInputStream
,它是 InputStream
的一个子类,用于从文件流中读取数据。
首先创建一个 readme.txt 文件,里面写上内容 AAA
然后编写代码读取并打印:
import java.io.FileInputStream;
import java.io.InputStream;
public class IODemo5FileInputStream {
public static void main(String[] args) {
InputStream input = null;
int n;
try {
input = new FileInputStream("readme.txt");
while (-1 != (n = input.read())) {
System.out.println(n);
}
} finally {
if(null != input)
input.close();
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
输出结果:65 65 65 (就是 A 的 ASCII 码)
# try(resource)
用 try ... finally
看起来代码有点复复杂,Java7 引入了 try(resource)
的语法,只需要编写 try
语句,让编译器自动为我们关闭资源:
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;
public class IODemo5FileInputStream2 {
public static void main(String[] args) throws IOException {
try (InputStream input = new FileInputStream("readme.txt")) {
int n;
while( -1 != (n = input.read())){
System.out.println(n);
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
实际上,编译器并不会特别地为 InputStream
加上自动关闭。编译器只看 try(resource = ...)
中的对象是否实现了 java.lang.AutoCloseable
接口,如果实现了,就自动加上 finally
语句并调用 close()
方法。
虽然 InputStream
和 OutputStream
实现的是 Closeable
接口,但该接口是继承了 AutoCloseable
的,因此,都可以用在 try(resource)
中。
# 缓冲
很多流提供了一次性读取多个字节到缓冲区的功能,这样效率高。InputStream
提供了两个重载方法来支持读取多个字节。我们需要先定义一个 byte[]
数组作为缓冲区:
int read(byte[] b)
:读取若干字节并填充到byte[]
数组,返回读取的字节数int read(byte[] b, int off, int len)
:指定byte[]
数组的偏移量和最大填充数
read()
方法会尽可能多地读取字节到缓冲区, 但不会超过缓冲区的大小。read()
方法的返回值不再是字节的 int
值,而是返回实际读取了多少个字节。如果返回 -1
,表示没有更多的数据了。
try (InputStream input = new FileInputStream("readme.txt")) {
byte[] buffer = new byte[100];
int n = input.read(buffer);
System.out.println(n); //3
}
2
3
4
5
# 阻塞
之前我们说过 InputStream
是同步 IO,因此在调用其相关方法的时候,我们说这些方法是阻塞(Blocking)的。
对于下面的代码:
int n;
n = input.read(); // 必须等待read()方法返回才能执行下一行代码
int m = n;
2
3
执行到第二行代码时,必须等 read()
方法返回后才能继续。因为读取 IO 流相比执行普通代码,速度会慢很多,因此,无法确定 read()
方法调用到底要花费多长时间。
# ByteArrayInputStream
除了用 FileInputStream
类来获取数据流,我们还可以用 ByteArrayInputStream
来在内存中模拟一个 InputStream
:
byte[] data = {114,5,14};
try (InputStream input = new ByteArrayInputStream(data)) {
int n;
while ((n = input.read()) != -1) {
System.out.println(n); //114,5,14
}
}
2
3
4
5
6
7
除了测试用,一般实际应用不多。
# 小结
知识点:
- Java 标准库的
java.io.InputStream
定义了所有输入流的超类 FileInputStream
实现了文件流输入ByteArrayInputStream
在内存中模拟一个字节流输入- 总是使用
try(resource)
来保证InputStream
正确关闭 - 声明输入流应该使用
InputStream
,而不是具体的类型(例如FileInputStream
),从而使得代码可以处理InputStream
的任意实现类
- 01
- 中国网络防火长城简史 转载10-12
- 03
- 公告:博客近期 RSS 相关问题10-02