简介
说明:FileInputStream流被称为文件字节输入流,继承了InputStream抽象类,用于对文件数据以字节的形式进行读取操作,如读取图片视频等
源码分析
package java.io;
import java.nio.channels.FileChannel;
import sun.nio.ch.FileChannelImpl;
/**
* A <code>FileInputStream</code> obtains input bytes
* from a file in a file system. What files
* are available depends on the host environment.
*
* <p><code>FileInputStream</code> is meant for reading streams of raw bytes
* such as image data. For reading streams of characters, consider using
* <code>FileReader</code>.
*
* @author Arthur van Hoff
* @see java.io.File
* @see java.io.FileDescriptor
* @see java.io.FileOutputStream
* @see java.nio.file.Files#newInputStream
* @since JDK1.0
*/
public class FileInputStream extends InputStream
{
/* File Descriptor - handle to the open file */
// 用于打开文件的文件描述符
private final FileDescriptor fd;
// 文件路径
private final String path;
// 文件通道
private FileChannel channel = null;
// 关闭文件输入流时用于同步锁机制的对象,保证线程安全
private final Object closeLock = new Object();
// 可见变量,标识当前输入流是否已经关闭
private volatile boolean closed = false;
/**
* 构造函数,传入文件路径名称
* @param name the system-dependent file name.
* @see java.lang.SecurityManager#checkRead(java.lang.String)
*/
public FileInputStream(String name) throws FileNotFoundException {
this(name != null ? new File(name) : null);
}
/**
* 构造函数,传入文件对象
* @param file the file to be opened for reading.
* @see java.io.File#getPath()
* @see java.lang.SecurityManager#checkRead(java.lang.String)
*/
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方法打开文件,内部调用一个native方法
open(name);
}
/**
* 构造方法,传入文件描述符类
* @param fdObj the file descriptor to be opened for reading.
* @see SecurityManager#checkRead(java.io.FileDescriptor)
*/
public FileInputStream(FileDescriptor fdObj) {
SecurityManager security = System.getSecurityManager();
if (fdObj == null) {
throw new NullPointerException();
}
if (security != null) {
security.checkRead(fdObj);
}
fd = fdObj;
path = null;
/*
*
*/
fd.attach(this);
}
/**
* native方法,打开目标路径的文件,以读取其内容
* @param name the name of the file
*/
private native void open0(String name) throws FileNotFoundException;
// wrap native call to allow instrumentation
/**
* 打开目标路径的文件
* @param name the name of the file
*/
private void open(String name) throws FileNotFoundException {
open0(name);
}
/**
* 从此输入流中读取一个数据字节。如果没有输入可用,则此方法将阻塞
* 返回-1表示已经到了文件末尾,无数据可读
* @return the next byte of data, or <code>-1</code> if the end of the
* file is reached.
*/
public int read() throws IOException {
return read0();
}
/**
* native方法,从目标文件读取一个字节
*/
private native int read0() throws IOException;
/**
* native方法,用于将数据读入字节数组
* @param b the data to be written
* @param off the start offset in the data
* @param len the number of bytes that are written
*/
private native int readBytes(byte b[], int off, int len) throws IOException;
/**
* 从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中,底层调用native方法
* @param b the buffer into which the data is read.
* @return the total number of bytes read into the buffer, or
* <code>-1</code> if there is no more data because the end of
* the file has been reached.
*/
public int read(byte b[]) throws IOException {
return readBytes(b, 0, b.length);
}
/**
* 从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。
* @param b the buffer into which the data is read.
* @param off the start offset in the destination array <code>b</code>
* @param len the maximum number of bytes read.
* @return the total number of bytes read into the buffer, or
* <code>-1</code> if there is no more data because the end of
* the file has been reached.
*/
public int read(byte b[], int off, int len) throws IOException {
return readBytes(b, off, len);
}
/**
* 实现父类方法,跳过指定字节数
* @param n the number of bytes to be skipped.
* @return the actual number of bytes skipped.
*/
public long skip(long n) throws IOException {
return skip0(n);
}
/**
* 本地方法,越过指定字节数
* @param n the number of bytes to be skipped.
* @return the actual number of bytes skipped.
*/
private native long skip0(long n) throws IOException;
/**
* 返回估计的剩余可读取字节数。
* @return an estimate of the number of remaining bytes that can be read
* (or skipped over) from this input stream without blocking.
*/
public int available() throws IOException {
return available0();
}
private native int available0() throws IOException;
/**
* 关闭字节流
* @revised 1.4
* @spec JSR-51
*/
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();
}
});
}
/**
* 获取当前输入流的文件描述符
* @return the file descriptor object associated with this stream.
* @see java.io.FileDescriptor
*/
public final FileDescriptor getFD() throws IOException {
if (fd != null) {
return fd;
}
throw new IOException();
}
/**
* 通过当前输入流打开通道并返回
* @return the file channel associated with this file input stream
* @since 1.4
* @spec JSR-51
*/
public FileChannel getChannel() {
// 同步方法
synchronized (this) {
if (channel == null) {
channel = FileChannelImpl.open(fd, path, true, false, this);
}
return channel;
}
}
// 设置类中(也就是FileInputStream)的属性的内存地址偏移量
private static native void initIDs();
// 本地方法,关闭输入流
private native void close0() throws IOException;
static {
initIDs();
}
/**
* 确保不再引用文件输入流时调用其 close 方法
* @see java.io.FileInputStream#close()
*/
protected void finalize() throws IOException {
if ((fd != null) && (fd != FileDescriptor.in)) {
/* if fd is shared, the references in FileDescriptor
* will ensure that finalizer is only called when
* safe to do so. All references using the fd have
* become unreachable. We can call close()
*/
close();
}
}
}
简单使用
现有一文件file.txt,内容如下:
package com.java.io;
import java.io.*;
/**
* @description InputStream 示例
* @date: 2021-01-17 15:01
*/
public class InputStreamExample {
public static void main(String[] args) {
commonExample();
}
public static void commonExample() {
// 文件路径
String filePath = "D:\\IDEAWorkSpace\\JAVALearn\\io\\src\\resource\\file.txt";
FileInputStream inputStream = null;
try {
// 根据文件路径构建文件输入流对象
inputStream = new FileInputStream(filePath);
byte[] bytes = new byte[1024];
int read = inputStream.read(bytes,0,10); // 读取10个字节并放入byte数组
int available = inputStream.available(); // 允许读取的剩余字节数
System.out.println("读取到了"+read+"个字节,还剩"+available+"个字节可读");
System.out.println("字节原始内容:"+ Arrays.toString(bytes));
System.out.println("读取到的内容:"+new String(bytes)); // 将字节数组转为普通字符串输出
// 读取剩余字节并存放到byte数组已有元素的后面【最后成为下标read开始的available个元素】
inputStream.read(bytes,read,available);
System.out.println("完整读取后:"+new String(bytes));
} catch (FileNotFoundException e) {
System.err.println("文件不存在");
} catch (IOException e) {
e.printStackTrace();
}finally {
if(null != inputStream){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
输出结果:
读取到了10个字节,还剩2个字节可读
字节原始内容:[66, 121, 101, 33, 66, 121, 101, 33, 66, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
读取到的内容:Bye!Bye!By
完整读取后:Bye!Bye!Bye!
思考
读取的字节为什么返回int型变量
1、read方法返回-1相当于是数据字典告诉调用者文件已到底,可以结束读取了,这里的-1是Int型
当文件未到底时,我们读取的是字节,若返回byte类型,那么势必造成**同一方法返回类型不同**的情况这是不允许的
2、我们读取的字节实际是由8位二进制组成,**二进制文件不利于直观查看**,可以转成常用的十进制进行展示,因此需要把读取的字节从二进制转成十进制整数,故返回int型
因此结合以上2点,保证**返回类型一致以及直观查看**的情况,因此该方法虽然读取的是字节但返回int型