接上篇文章java io -- InputStream,本文将剖析InputStream中最常用的一个子类FileInputStream
FileInputStream是对一个文件抽象而成的数据流,我们这里先展示一下这个类的结构。
可以看到,这个结构和InputStream大致一致,不过多了一些特性。
首先这个类的构造函数,可以通过String或File进行构造一个FileInputStream对象,但是需要注意,如果路径错误,没有对应的文件,会抛出异常。
下面我们将从源码的角度,看几个重要的方法。
由于读取文件时,首先需要打开文件,因此多了open()方法,为了简化读取操作,又增加了几个构造方法。
private native void open0(String name) throws FileNotFoundException;
private void open(String name) throws FileNotFoundException {
open0(name);
}
打开文件,最终用的都是open0()这个方法,这个方法是个native方法。
public int read() throws IOException {
return read0();
}
private native int read0() throws IOException;
读取文件也最终时是一个native方法,不过请参考上一篇文章中,对InputStream的说明,这个read()也必然是一个block的方法,不过由于时间短,我们感受不到罢了。
这个read()方法的实现中,最终是利用文件句柄读取了文件内容,有兴趣的可以去看c++源码。
FileInputStream的使用示例:
我这里用一个简单的例子说明一下,这里用到了FileInputStream,代表我们从一个目录中读取文件中的内容。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
/**
* Created by zhaohui on 16-10-15.
*/
public class InputRead {
public static void main(String[] args) {
try {
InputStream in = new FileInputStream("/home/zhaohui/tmp/zhaohui");
int length = 0;
//------------ 单字节读取 ----------------
// 循环读取文件,每次读取一个字节,如果读到文件末尾,返回-1
int b;
for (;(b=in.read())>0;){
length ++;
}
in.close();
System.out.println("the length is " + length);
// ---------- 多字节读取 -------------------
// 将文件中数据读满(如果足够)字节数组
//length = 0;
in = new FileInputStream("/home/zhaohui/tmp/zhaohui");
byte[] array1 = new byte[100];
length = in.read(array1);
in.close();
System.out.println("the length is " +length);
// ------------ 从流中读取的字节数太多 -----------
byte[] array = new byte[1];
in.read(array, 0, 3);
in.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
}
}
}
Exception in thread "main" java.lang.IndexOutOfBoundsException
at java.io.FileInputStream.readBytes(Native Method)
at java.io.FileInputStream.read(FileInputStream.java:255)
at InputRead.main(InputRead.java:36)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
the length is 10
the length is 10
上面的例子,说明了inputStream的两种用法,一次读取一个字节和直接读取多个字节,以及一个错误的示例,如果读取出的字节数太多,容纳数据的数组太小,就会抛出异常。
我的总结:
1)InputStream作为java.io包中一个top class,定义了众多的抽象方法,比如read(),作为template模式,也实现了读取多个字节的read方法。
2)FileInputStream作为InputStream的一个子类,不仅实现了read方法,并根据自身特点添加了特性。
由于InputStream有众多的子类,我们最常用的就是文件流,因此这里只对FileInputStream做了介绍,其他的请自行查阅官方文档。
下一篇文章将介绍一个特殊的InputStream子类,FilterInputStream。