一、FileInputStream介绍
FileInputStream基于文件的字节输入流,继承自InputStream,它可以从文件系统的文件中创建字节流,判断哪些文件可用与主机环境有关。
二、FileInputStream源码解析
1)FileInputStream的属性
private final FileDescriptor fd;//文件描述符
private final String path;//文件路径
private FileChannel channel;//文件通道
private final Object closeLock = new Object();//关闭文件流的锁
private volatile boolean closed = false;//FileInputStream文件流的关闭状态
2)构造方法源码
public FileInputStream(String name) throws FileNotFoundException {
this(name != null ? new File(name) : null);
}
public FileInputStream(File file) throws FileNotFoundException {
String name = (file != null ? file.getPath() : null);
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead(name);
}
if (name == null) {
throw new NullPointerException();
}
if (file.isInvalid()) {
throw new FileNotFoundException("Invalid file path");
}
fd = new FileDescriptor();
fd.attach(this);
path = name;
open(name);
}
public FileInputStream(FileDescriptor fdObj) {
SecurityManager security = System.getSecurityManager();
if (fdObj == null) {
throw new NullPointerException();
}
if (security != null) {
security.checkRead(fdObj);
}
fd = fdObj;
path = null;
/*
* FileDescriptor is being shared by streams.
* Register this stream with FileDescriptor tracker.
*/
fd.attach(this);
}
FileInputStream的构造方法有两个,构造方法1)会根据路径字符串创建File对象调用构造方法2)我们直接分析构造方法2)的代码,发现构造方法2)主要做了以下 几件事:
1 - 从file对象中获取文件名称(包含全路径)
2 - 获取SercurityManager系统安全管理器,如果不为空校验文件的读取权限
3 - 文件路径判空,为空抛出空指针异常
4 - 文件路径有效性判断,若为空或者文件路径不存在则抛出FileNotFoundException
5 - 文件描述符fd关联当前输入流,设置文件路径path,并打开指定文件准备读取
构造方法3)逻辑很简单就不分析了,但是要注意如果时根据文件描述符调用FileInputStream构造方法,此时open方法未调用字节读取准备还未就绪。
3)其他成员方法
int read()方法
public int read() throws IOException {
return read0();
}
private native int read0() throws IOException;
read方法内部调用了一个native本地方法,这部分方法的具体源码需要我们去下载相应版本的open jdk,阅读open jdk源码我们可知该方法每次从当前输入流中读取一个字节数据,如果输入不可用,则该方法将阻塞。
int read(byte[] b, int off, int len)方法
private native int readBytes(byte b[], int off, int len) throws IOException;
public int read(byte b[]) throws IOException {
return readBytes(b, 0, b.length);
}
public int read(byte b[], int off, int len) throws IOException {
return readBytes(b, off, len);
}
该方法底层也是调用一个本地native方法readBytes,主要用于将指定最大长度的字节数据读入字节数组b中,并返回读取的实际字节大小。
long skip(long n)方法
/**
* Skips over and discards <code>n</code> bytes of data from the
* input stream.
*
* <p>The <code>skip</code> method may, for a variety of
* reasons, end up skipping over some smaller number of bytes,
* possibly <code>0</code>. If <code>n</code> is negative, the method
* will try to skip backwards. In case the backing file does not support
* backward skip at its current position, an <code>IOException</code> is
* thrown. The actual number of bytes skipped is returned. If it skips
* forwards, it returns a positive value. If it skips backwards, it
* returns a negative value.
*
* <p>This method may skip more bytes than what are remaining in the
* backing file. This produces no exception and the number of bytes skipped
* may include some number of bytes that were beyond the EOF of the
* backing file. Attempting to read from the stream after skipping past
* the end will result in -1 indicating the end of the file.
*
* @param n the number of bytes to be skipped.
* @return the actual number of bytes skipped.
* @exception IOException if n is negative, if the stream does not
* support seek, or if an I/O error occurs.
*/
public native long skip(long n) throws IOException;
skip方法也是一个native本地方法,它的主要用于在文件输入流中跳过指定长度的字节,如果参数n是负数那么输入流将会尝试向后跳转,如果文件不支持往后跳转将抛出IO异常,如果往前跳转将返回正整数,往后跳转将返回负整数,本方法传入的跳转字节数n可以超过文件的最大可读字节数。当n设置超出文件的可读字节数继续读取将直接返回-1表示已经到达文件末尾。
int available()方法
/**
* Returns an estimate of the number of remaining bytes that can be read (or
* skipped over) from this input stream without blocking by the next
* invocation of a method for this input stream. Returns 0 when the file
* position is beyond EOF. The next invocation might be the same thread
* or another thread. A single read or skip of this many bytes will not
* block, but may read or skip fewer bytes.
*
* <p> In some cases, a non-blocking read (or skip) may appear to be
* blocked when it is merely slow, for example when reading large
* files over slow networks.
*
* @return an estimate of the number of remaining bytes that can be read
* (or skipped over) from this input stream without blocking.
* @exception IOException if this file input stream has been closed by calling
* {@code close} or an I/O error occurs.
*/
public native int available() throws IOException;
native方法,返回本文件输入流中非阻塞的可读的字节数,当文件输入流的当前读取位置超出文件末尾时,返回0表示无可读数据。
void close方法
/**
* 关闭文件输入流并释放相关的系统资源
* 如果文件输入流与文件通道channel关联那么channel也要关闭
* IO错误时抛出异常
*/
public void close() throws IOException {
synchronized (closeLock) {
if (closed) {
return;
}
closed = true;
}
if (channel != null) {
channel.close();
}
fd.closeAll(new Closeable() {
public void close() throws IOException {
close0();
}
});
}
close方法先更新文件流关闭状态closed为true关闭状态,然后在关联通道channel不为空时调用他的close方法关闭通道,最后借助文件描述符fd在读取完成时关闭流并释放相关系统资源。我们注意到更新文件输入流关闭状态closed的时候使用了一个closeLock的对象锁对该段代码块进行加锁,这也是我们提倡的锁优化一般性原则,减少加锁粒度和范围,对方法中一段代码块加锁相比对整个方法加锁更可取,并发度也更高,当然也有例外情况,这里暂不讨论。
final FileDescriptor getFd()方法
public final FileDescriptor getFD() throws IOException {
if (fd != null) {
return fd;
}
throw new IOException();
}
返回与当前文件输入流关联的文件描述符,如果文件描述符为空抛出IO异常
FileChannel getChannel()方法
返回此输入流相关的唯一Filechannel对象
void finalize()
protected void finalize() throws IOException {
if ((fd != null) && (fd != FileDescriptor.in)) {
close();
}
}
确保当没有引用指向当前文件输入流的时候调用他的close方法释放资源