简介

说明:FileInputStream流被称为文件字节输入流,继承了InputStream抽象类,用于对文件数据以字节的形式进行读取操作,如读取图片视频等

源码分析

  1. package java.io;
  2. import java.nio.channels.FileChannel;
  3. import sun.nio.ch.FileChannelImpl;
  4. /**
  5. * A <code>FileInputStream</code> obtains input bytes
  6. * from a file in a file system. What files
  7. * are available depends on the host environment.
  8. *
  9. * <p><code>FileInputStream</code> is meant for reading streams of raw bytes
  10. * such as image data. For reading streams of characters, consider using
  11. * <code>FileReader</code>.
  12. *
  13. * @author Arthur van Hoff
  14. * @see java.io.File
  15. * @see java.io.FileDescriptor
  16. * @see java.io.FileOutputStream
  17. * @see java.nio.file.Files#newInputStream
  18. * @since JDK1.0
  19. */
  20. public class FileInputStream extends InputStream
  21. {
  22. /* File Descriptor - handle to the open file */
  23. // 用于打开文件的文件描述符
  24. private final FileDescriptor fd;
  25. // 文件路径
  26. private final String path;
  27. // 文件通道
  28. private FileChannel channel = null;
  29. // 关闭文件输入流时用于同步锁机制的对象,保证线程安全
  30. private final Object closeLock = new Object();
  31. // 可见变量,标识当前输入流是否已经关闭
  32. private volatile boolean closed = false;
  33. /**
  34. * 构造函数,传入文件路径名称
  35. * @param name the system-dependent file name.
  36. * @see java.lang.SecurityManager#checkRead(java.lang.String)
  37. */
  38. public FileInputStream(String name) throws FileNotFoundException {
  39. this(name != null ? new File(name) : null);
  40. }
  41. /**
  42. * 构造函数,传入文件对象
  43. * @param file the file to be opened for reading.
  44. * @see java.io.File#getPath()
  45. * @see java.lang.SecurityManager#checkRead(java.lang.String)
  46. */
  47. public FileInputStream(File file) throws FileNotFoundException {
  48. String name = (file != null ? file.getPath() : null);
  49. // 验证系统权限
  50. SecurityManager security = System.getSecurityManager();
  51. if (security != null) {
  52. security.checkRead(name); // 验证路径是否合法且权限允许
  53. }
  54. if (name == null) {
  55. throw new NullPointerException();
  56. }
  57. // 验证文件是否有效
  58. if (file.isInvalid()) {
  59. throw new FileNotFoundException("Invalid file path");
  60. }
  61. // 描述符赋值
  62. fd = new FileDescriptor();
  63. fd.attach(this);
  64. // 文件路径赋值
  65. path = name;
  66. // 调用open方法打开文件,内部调用一个native方法
  67. open(name);
  68. }
  69. /**
  70. * 构造方法,传入文件描述符类
  71. * @param fdObj the file descriptor to be opened for reading.
  72. * @see SecurityManager#checkRead(java.io.FileDescriptor)
  73. */
  74. public FileInputStream(FileDescriptor fdObj) {
  75. SecurityManager security = System.getSecurityManager();
  76. if (fdObj == null) {
  77. throw new NullPointerException();
  78. }
  79. if (security != null) {
  80. security.checkRead(fdObj);
  81. }
  82. fd = fdObj;
  83. path = null;
  84. /*
  85. *
  86. */
  87. fd.attach(this);
  88. }
  89. /**
  90. * native方法,打开目标路径的文件,以读取其内容
  91. * @param name the name of the file
  92. */
  93. private native void open0(String name) throws FileNotFoundException;
  94. // wrap native call to allow instrumentation
  95. /**
  96. * 打开目标路径的文件
  97. * @param name the name of the file
  98. */
  99. private void open(String name) throws FileNotFoundException {
  100. open0(name);
  101. }
  102. /**
  103. * 从此输入流中读取一个数据字节。如果没有输入可用,则此方法将阻塞
  104. * 返回-1表示已经到了文件末尾,无数据可读
  105. * @return the next byte of data, or <code>-1</code> if the end of the
  106. * file is reached.
  107. */
  108. public int read() throws IOException {
  109. return read0();
  110. }
  111. /**
  112. * native方法,从目标文件读取一个字节
  113. */
  114. private native int read0() throws IOException;
  115. /**
  116. * native方法,用于将数据读入字节数组
  117. * @param b the data to be written
  118. * @param off the start offset in the data
  119. * @param len the number of bytes that are written
  120. */
  121. private native int readBytes(byte b[], int off, int len) throws IOException;
  122. /**
  123. * 从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中,底层调用native方法
  124. * @param b the buffer into which the data is read.
  125. * @return the total number of bytes read into the buffer, or
  126. * <code>-1</code> if there is no more data because the end of
  127. * the file has been reached.
  128. */
  129. public int read(byte b[]) throws IOException {
  130. return readBytes(b, 0, b.length);
  131. }
  132. /**
  133. * 从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。
  134. * @param b the buffer into which the data is read.
  135. * @param off the start offset in the destination array <code>b</code>
  136. * @param len the maximum number of bytes read.
  137. * @return the total number of bytes read into the buffer, or
  138. * <code>-1</code> if there is no more data because the end of
  139. * the file has been reached.
  140. */
  141. public int read(byte b[], int off, int len) throws IOException {
  142. return readBytes(b, off, len);
  143. }
  144. /**
  145. * 实现父类方法,跳过指定字节数
  146. * @param n the number of bytes to be skipped.
  147. * @return the actual number of bytes skipped.
  148. */
  149. public long skip(long n) throws IOException {
  150. return skip0(n);
  151. }
  152. /**
  153. * 本地方法,越过指定字节数
  154. * @param n the number of bytes to be skipped.
  155. * @return the actual number of bytes skipped.
  156. */
  157. private native long skip0(long n) throws IOException;
  158. /**
  159. * 返回估计的剩余可读取字节数。
  160. * @return an estimate of the number of remaining bytes that can be read
  161. * (or skipped over) from this input stream without blocking.
  162. */
  163. public int available() throws IOException {
  164. return available0();
  165. }
  166. private native int available0() throws IOException;
  167. /**
  168. * 关闭字节流
  169. * @revised 1.4
  170. * @spec JSR-51
  171. */
  172. public void close() throws IOException {
  173. // 同步锁机制
  174. synchronized (closeLock) {
  175. if (closed) {
  176. return;
  177. }
  178. closed = true;
  179. }
  180. // 关闭通道
  181. if (channel != null) {
  182. channel.close();
  183. }
  184. // 关闭所有与描述符相关的资源
  185. fd.closeAll(new Closeable() {
  186. public void close() throws IOException {
  187. close0();
  188. }
  189. });
  190. }
  191. /**
  192. * 获取当前输入流的文件描述符
  193. * @return the file descriptor object associated with this stream.
  194. * @see java.io.FileDescriptor
  195. */
  196. public final FileDescriptor getFD() throws IOException {
  197. if (fd != null) {
  198. return fd;
  199. }
  200. throw new IOException();
  201. }
  202. /**
  203. * 通过当前输入流打开通道并返回
  204. * @return the file channel associated with this file input stream
  205. * @since 1.4
  206. * @spec JSR-51
  207. */
  208. public FileChannel getChannel() {
  209. // 同步方法
  210. synchronized (this) {
  211. if (channel == null) {
  212. channel = FileChannelImpl.open(fd, path, true, false, this);
  213. }
  214. return channel;
  215. }
  216. }
  217. // 设置类中(也就是FileInputStream)的属性的内存地址偏移量
  218. private static native void initIDs();
  219. // 本地方法,关闭输入流
  220. private native void close0() throws IOException;
  221. static {
  222. initIDs();
  223. }
  224. /**
  225. * 确保不再引用文件输入流时调用其 close 方法
  226. * @see java.io.FileInputStream#close()
  227. */
  228. protected void finalize() throws IOException {
  229. if ((fd != null) && (fd != FileDescriptor.in)) {
  230. /* if fd is shared, the references in FileDescriptor
  231. * will ensure that finalizer is only called when
  232. * safe to do so. All references using the fd have
  233. * become unreachable. We can call close()
  234. */
  235. close();
  236. }
  237. }
  238. }

简单使用

现有一文件file.txt,内容如下:
图片.png

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型