一、阻塞式IO模型

Java IO是Java自带的 API,用于读取和写入数据(输入和输出)。大多数应用程序需要处理一些输入并根据该输入产生一些输出。例如,从文件或通过网络读取数据,然后通过网络写入文件或写回响应。
Java IO API 位于 Java IO 包 ( java.io) 中。jdk->rt.jar-java.io包下面:
image.png

Java IO (java.io) 包的作用域

该java.io包实际上并未解决所有类型的输入和输出。例如,Java IO 包中不包括 GUI 或网页的输入和输出。这些类型的输入在其他地方都有介绍,例如 Swing 项目中的 JFC 类,或者Java Enterprise Edition 中的Servlet和 HTTP 包。
Java IO 包主要关注文件、网络流、内部内存缓冲区等的输入和输出。但是,Java IO 包不包含用于打开网络通信所必需的网络套接字的类。为此,您需要使用Java Networking API。但是,一旦您打开了一个套接字(网络连接),您就可以通过 Java IOInputStream和OutputStream类在其中读取和写入数据。

Java NIO - 替代 IO API

Java 还包含另一个称为Java NIO 的IO API 。它包含的类与 Java IO 和 Java Networking API 的功能大致相同,但 Java NIO 可以在非阻塞模式下工作。在某些情况下,非阻塞 IO 可以提供比阻塞 IO 更大的性能提升。

IO Streams 流

IO Streams 是Java IO 中的核心概念。流是概念上无限的数据流。您可以从流中读取或写入流。流连接到数据源或数据目标。Java IO 中的流可以基于字节(读取和写入字节)或基于字符(读取和写入字符)。
InputStream、OutputStream、Reader 和 Writer
需要从某个源读取数据的程序需要一个InputStream或 一个Reader。需要将数据写入某个目的地的程序需要一个 OutputStream或Writer。下图也说明了这一点:
image.png
一个InputStream或Reader链接到源数据。一个OutputStream 或Writer链接到数据的目的地。

Java IO 的用途和特性

Java的IO包含很多子类InputStream,OutputStream,Reader 和Writer类。原因是,所有这些子类都针对各种不同的目的。这就是为什么有这么多不同的类。所涉及的目的总结如下: :::info

  • 文件访问 File Access
  • 网络接入 Network Access
  • 内部存储器缓冲区访问 Internal Memory Buffer Access
  • 线程间通信(管道) Inter-Thread Communication (Pipes)
  • 缓冲 Buffering
  • 过滤 Filtering
  • 解析 Parsing
  • 阅读和写作文本(读者/作家) Reading and Writing Text (Readers / Writers)
  • 读取和写入原始数据(long、int 等) Reading and Writing Primitive Data (long, int etc.)
  • 读写对象 Reading and Writing Objects ::: 在通读 Java IO 类时了解这些目的是很好的。它们使理解类的目标更容易一些。

    Java IO 类概览表

    |
    | 基于字节输入 | 基于字节输出 | 基于字符输入 | 基于字符输出 | | —- | —- | —- | —- | —- | | 基本的 | InputStream | OutputStream | ReaderInputStreamReader | WriterOutputStreamWriter | | 数组 | ByteArrayInputStream | ByteArrayOutputStream | CharArrayReader | CharArrayWriter | | 文件 | FileInputStreamRandomAccessFile | FileOutputStreamRandomAccessFile | FileReader | FileWriter | | 管道 | PipedInputStream | PipedOutputStream | PipedReader | PipedWriter | | 缓冲 | BufferedInputStream | BufferedOutputStream | BufferedReader | BufferedWriter | | 过滤 | FilterInputStream | FilterOutputStream | FilterReader | FilterWriter | | 解析 | PushbackInputStreamStreamTokenizer | | PushbackReaderLineNumberReader | | | 字符串 | | | StringReader | StringWriter | | Data | DataInputStream | DataOutputStream | | | | Object | ObjectInputStream | ObjectOutputStream | | |

二、Java IO: Files

文件是Java 应用程序中数据的常见来源或目的地。因此,本文将简要概述在 Java 中处理文件。这里并不是要详细解释每一种技术,而是要为您提供足够的知识来决定文件访问方法。单独的页面将更详细地描述这些方法或类中的每一个,包括它们的用法示例等。

Java IO 文件类

Java IO API 包含以下与在 Java 中处理文件相关的类: :::info

  • 文件 File
  • 随机存取文件 RandomAccessFile
  • 文件输入流 FileInputStream
  • 文件阅读器 FileReader
  • 文件输出流 FileOutputStream
  • 文件写入器 FileWriter :::

    通过Java IO 读取文件

    如果您需要从一端读取文件到另一端,您可以使用 FileInputStream 或FileReader具体取决于您是要将文件作为二进制数据还是文本数据读取。这两个类使您可以从文件的开头到结尾一次一个字节或一个字符地读取文件,或者将字节读入byteor数组char,再次从文件的开头到结尾。您不必读取整个文件,但您只能按字节和字符存储在文件中的顺序读取它们。
    如果您需要跳过文件并从这里和那里只读取其中的一部分,您可以使用 RandomAccessFile.

    通过 Java IO 写入文件

    如果您需要将文件从一端写入另一端,则可以使用 FileOutputStream 或FileWriter具体取决于您是需要写入二进制数据还是字符。您可以从文件的开头到结尾一次写入一个字节或字符,或者写入byte 和 的数组char。数据按照写入的顺序依次存储在文件中。
    如果你需要跳过一个文件并在不同的地方写入它,例如附加到文件的末尾,你可以使用RandomAccessFile.

    三、Java 文件输入输出流分析及使用

    Java FileInputStream 示例

    在Java 的FileInputStream类java.io.FileInputStream,能够读取文件的内容作为字节流。JavaFileInputStream类是Java InputStream的子类。这意味着您将 JavaFileInputStream用作InputStream (FileInputStream行为类似于InputStream)。
    这是一个简单的FileInputStream例子:

    1. public class test {
    2. public static void main(String[] args) {
    3. InputStream input=null;
    4. try {
    5. input = new FileInputStream("C:\\IO_Test\\FileInputStream.txt");
    6. byte[] data=new byte[1024];
    7. int i=0;
    8. while((i=input.read(data))!=-1){
    9. System.out.println(new String(data,0,i));
    10. }
    11. } catch (Exception e) {
    12. e.printStackTrace();
    13. } finally {
    14. try {
    15. input.close();
    16. } catch (IOException e) {
    17. e.printStackTrace();
    18. }
    19. }
    20. }
    21. }

    输出结果:
    image.png

    FileInputStream 构造函数

    该FileInputStream你可以用它来创建三个不同的构造函数FileInputStream 实例。我将在这里介绍前两个。
    第一个构造函数将 String作为参数。这String应该包含文件系统中要读取的文件所在的路径。这是一个代码示例:

    1. String path = "C:\\user\\data\\qingfeng.txt";
    2. FileInputStream fileInputStream = new FileInputStream(path);

    请注意path String. 它需要双反斜杠 ( ) 在 中创建单个反斜杠String,因为反斜杠是 Java 字符串中的转义字符。要获得单个反斜杠,您需要使用转义序列\。
    在linux上,路径如下所示:

    1. String path = "/home/qingfeng/data/qingfeng.txt";

    第二个FileInputStream构造函数接受一个File对象作为参数。该File 对象必须指向您要读取的文件。下面是一个例子:

    1. String path = "C:\\user\\data\\qingfeng.txt";
    2. File file =new Filepath);
    3. FileInputStream fileInputStream = new FileInputStream(file);

    读取性能

    一次读取一个字节数组比一次从 Java 中读取一个字节要快FileInputStream。通过读取字节数组而不是一次读取一个字节,差异很容易使性能提高 10 倍或更多。
    获得的确切加速取决于您读取的字节数组的大小,以及运行代码的计算机的操作系统、硬件等。在决定之前,您应该研究目标系统的硬盘缓冲区大小等。然而,8KB 及以上的缓冲区大小将提供良好的加速。但是,一旦您的字节数组超过了底层操作系统和硬件的容量,您将不会从更大的字节数组中获得更大的加速。
    您可能需要尝试不同的字节数组大小并测量读取性能,以找到最佳的字节数组大小。

    通过 BufferedInputStream 缓冲读取

    您可以FileInputStream 使用Java BufferedInputStream添加对字节数组的透明、自动读取和缓冲。所述BufferedInputStream 读取字节的块成从底层的字节数组FileInputStream。然后,您可以从 中一个一个地读取字节,BufferedInputStream并且仍然可以获得大量来自读取字节数组而不是一次一个字节的加速。这是将 Java 包装FileInputStream在 a 中的示例BufferedInputStream: ```basic InputStream input = new BufferedInputStream( new FileInputStream(“c:\data\qingfeng.txt”), 1024 1024 / 缓冲区大小 */ );

  1. 请注意, BufferedInputStream是一个InputStream子类,可以在任何可以以InputStream的方式使用。
  2. <a name="VfKxt"></a>
  3. ## 关闭FileInputStream
  4. 当您完成从 Java 读取数据时,FileInputStream您必须关闭它。您可以FileInputStream通过调用从close()继承的方法来关闭 a InputStream。这是打开 a FileInputStream,从中读取所有数据,然后关闭它的示例:
  5. ```basic
  6. try( FileInputStream fileInputStream = new FileInputStream("file.txt") ) {
  7. int data = fileInputStream.read();
  8. while(data != -1){
  9. data = fileInputStream.read();
  10. }
  11. }

一旦正在执行的线程退出该try块,该块FileInputStream就会关闭。如果从try块内部抛出异常,则捕获异常, FileInputStream关闭该异常,然后重新抛出异常。因此FileInputStream,当在 try-with-resources 块中使用时,可以保证 是关闭的。

将 FileInputStream 转换为 Reader

JavaFileInputStream是基于字节的数据流。您可能知道,Java IO API 也有一组基于字符的输入流,称为“读取器”。您可以在Java转换FileInputStream 为JavaReader使用Java的InputStreamReader中。你可以阅读更多关于如何使用InputStreamReader通过单击前一句的链接,但这里是将一个Java的一个简单的例子FileInputStream来的InputStreamReader:

  1. InputStream inputStream = new FileInputStream("c:\\data\\input.txt");
  2. Reader inputStreamReader = new InputStreamReader(inputStream);

四、FileOutputStream文件输出流

在Java的 FileOutputStream中类java.io.FileOutputStream,使得它可以写一个文件的字节流。JavaFileOutputStream类是Java OutputStream的子类, 这意味着您可以将 一个FileOutputStream作为OutputStream。

FileOutputStream 示例

  1. public class test {
  2. public static void main(String[] args) {
  3. OutputStream outputStream=null;
  4. try {
  5. outputStream =new FileOutputStream("C:\\\\IO_Test\\\\FileInputStream.txt",true);
  6. String str="hello java;你好 世界";
  7. byte[] bytes = str.getBytes();
  8. outputStream.write(bytes);
  9. outputStream.flush();
  10. } catch (Exception e) {
  11. e.printStackTrace();
  12. }finally {
  13. try {
  14. outputStream.close();
  15. } catch (IOException e) {
  16. e.printStackTrace();
  17. }
  18. }
  19. }
  20. }

FileOutputStream 构造函数

FileOutputStream 的构造函数与FileInputStream类似,不过它多了一个有用的参数,你可以通过传出true/false来决定是否在原文件的基础上进行写入。

  1. OutputStream output = new FileOutputStream("c:\\data\\output-text.txt", true); //append
  2. OutputStream output = new FileOutputStream("c:\\data\\output-text.txt", false); //覆盖

当您省略第二个boolean参数并因此仅使用采用文件路径的构造函数时,默认模式是覆盖给定路径上的任何现有文件。

写入性能

将字节数组写入 Java FileOutputStream 比一次写入一个字节要快。加速可能非常显着 - 高达 10 倍或更多。因此,建议尽可能使用这些write(byte[]) 方法。
您获得的确切加速取决于您运行 Java 代码的计算机的底层操作系统和硬件。加速取决于内存速度、硬盘速度和缓冲区大小等问题。

Flash 冲洗

当您将数据写入 Java 时FileOutputStream,数据可能会在内部缓存在计算机内存中,并在稍后写入磁盘。例如,每次有 X 量的数据要写入,或FileOutputStream关闭时。
如果你想确保所有写入的数据都写入磁盘而不必关闭FileOutputStream 你可以调用它的flush()方法。调用flush()将确保FileOutputStream到目前为止已写入的所有数据也完全写入磁盘。下面是调用 JavaFileOutputStream flush()方法的示例:

  1. OutputStream outputStream = new FileOutputStream("c:\\data\\output-text.txt");
  2. Byte bytes[]=new byte[]{1,2,3,4,5};
  3. outputStream.write(bytes);
  4. outputStream.flush()