简介
说明: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型
