Java中的I/O系统中的字节流

## 一、简介
在Java的I/O体系中,节点流是直接与数据源或数据汇(目的地)相连的流。它们构成了数据传输的最底层操作,是构建更复杂的I/O操作的基础。理解节点流对于深入掌握Java的I/O系统至关重要。

## 二、文件相关的节点流

### 1. `FileInputStream`
 - **功能与特点**
   - `FileInputStream`是字节输入节点流,用于从文件中读取字节数据。它直接与文件系统中的文件建立连接,按照字节顺序读取文件内容。这意味着它可以处理任何类型的文件,无论是文本文件、二进制文件(如图片、音频等)。
   - 例如,当读取一个二进制图像文件时,`FileInputStream`可以逐个字节地获取文件中的数据,而不需要对文件内容进行任何特殊的解释(因为它只是处理字节)。
 - **示例代码**
   ```java
   import java.io.File;
   import java.io.FileInputStream;
   import java.io.IOException;

   public class FileInputStreamExample {
       public static void main(String[] args) {
           try {
               File file = new File("example.txt");
               FileInputStream fis = new FileInputStream(file);
               int data;
               while ((data = fis.read())!= -1) {
                   System.out.print((char) data);
               }
               fis.close();
           } catch (IOException e) {
               e.printStackTrace();
           }
       }
   }
   ```
   - 在这个示例中,我们创建了一个`FileInputStream`对象来读取`example.txt`文件。通过`read`方法逐个字节地读取文件内容,直到到达文件末尾(`read`返回 -1)。

### 2. `FileOutputStream`
 - **功能与特点**
   - `FileOutputStream`是字节输出节点流,用于将字节数据写入文件。它直接操作文件系统中的文件,将字节流按照顺序写入文件。如果文件不存在,它可以创建新文件;如果文件已经存在,根据构造函数的不同参数设置,可以选择覆盖原有文件内容或者追加内容到文件末尾。
   - 例如,当我们想要保存从网络上下载的二进制文件(如一个软件安装包)到本地磁盘时,可以使用`FileOutputStream`将字节数据写入到指定的文件中。
 - **示例代码**
   ```java
   import java.io.File;
   import java.io.FileOutputStream;
   import java.io.IOException;

   public class FileOutputStreamExample {
       public static void main(String[] args) {
           try {
               File file = new File("output.txt");
               FileOutputStream fos = new FileOutputStream(file);
               String data = "Hello, World!";
               byte[] byteData = data.getBytes();
               fos.write(byteData);
               fos.close();
           } catch (IOException e) {
               e.printStackTrace();
           }
       }
   }
   ```
   - 这里我们创建了一个`FileOutputStream`对象来写入`output.txt`文件。将字符串"Hello, World!"转换为字节数组后,使用`write`方法将字节数据写入文件。

### 3. `FileReader`
 - **功能与特点**
   - `FileReader`是字符输入节点流,专门用于读取文本文件中的字符数据。与`FileInputStream`不同,`FileReader`在读取数据时会考虑字符编码,能够正确地将字节转换为字符。它以字符为单位读取文件内容,使得处理文本文件更加方便和直观。
   - 例如,当读取一个包含中文字符的文本文件时,`FileReader`可以正确地将字节按照合适的编码(如UTF - 8)转换为字符,而不会出现乱码问题(前提是编码设置正确)。
 - **示例代码**
   ```java
   import java.io.File;
   import java.io.FileReader;
   import java.io.IOException;

   public class FileReaderExample {
       public static void main(String[] args) {
           try {
               File file = new File("example.txt");
               FileReader fr = new FileReader(file);
               int data;
               while ((data = fr.read())!= -1) {
                   System.out.print((char) data);
               }
               fr.close();
           } catch (IOException e) {
               e.printStackTrace();
           }
       }
   }
   ```
   - 在这个示例中,`FileReader`从`example.txt`文本文件中逐个字符地读取数据,直到文件末尾。

### 4. `FileWriter`
 - **功能与特点**
   - `FileWriter`是字符输出节点流,用于将字符数据写入文本文件。它同样会根据系统默认的字符编码(或者可以指定编码)将字符转换为字节后写入文件。如果文件不存在,会创建新文件;如果文件存在,默认会覆盖原有文件内容,但也可以设置为追加模式。
   - 例如,当我们想要记录一些日志信息到文本文件时,可以使用`FileWriter`将字符形式的日志内容写入到文件中。
 - **示例代码**
   ```java
   import java.io.File;
   import java.io.FileWriter;
   import java.io.IOException;

   public class FileWriterExample {
       public static void main(String[] args) {
           try {
               File file = new File("output.txt");
               FileWriter fw = new FileWriter(file);
               fw.write("This is a sample text.");
               fw.close();
           } catch (IOException e) {
               e.printStackTrace();
           }
       }
   }
   ```
   - 这里`FileWriter`将指定的字符串写入到`output.txt`文件中。

## 三、数组相关的节点流

### 1. `ByteArrayInputStream`
 - **功能与特点**
   - `ByteArrayInputStream`是字节输入节点流,它的数据源是一个字节数组。这在处理内存中的字节数据时非常有用,例如,当我们已经将一些数据读取到字节数组中,想要对这些数据进行类似文件读取的操作(如按照顺序逐个字节处理)时,可以使用`ByteArrayInputStream`。
   - 它允许我们将字节数组视为一个输入流的数据源,从而可以复用很多针对输入流的操作方法。
 - **示例代码**
   ```java
   import java.io.ByteArrayInputStream;

   public class ByteArrayInputStreamExample {
       public static void main(String[] args) {
           byte[] byteArray = {65, 66, 67};
           ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
           int data;
           while ((data = bais.read())!= -1) {
               System.out.print((char) data);
           }
       }
   }
   ```
   - 在这个示例中,我们创建了一个包含字符'A'、'B'、'C'(对应的ASCII码值)的字节数组,然后使用`ByteArrayInputStream`将字节数组作为数据源进行读取操作。

### 2. `ByteArrayOutputStream`
 - **功能与特点**
   - `ByteArrayOutputStream`是字节输出节点流,它将字节数据输出到一个字节数组中。这在需要在内存中构建字节数据时非常有用,例如,当我们想要动态地生成一些字节数据(如将多个小的字节数据片段组合成一个大的字节数组)时,可以使用`ByteArrayOutputStream`。
   - 它提供了一种方便的方式来操作字节数据,最后可以通过`toByteArray`方法获取生成的字节数组。
 - **示例代码**
   ```java
   import java.io.ByteArrayOutputStream;

   public class ByteArrayOutputStreamExample {
       public static void main(String[] args) {
           ByteArrayOutputStream baos = new ByteArrayOutputStream();
           try {
               baos.write(65);
               baos.write(66);
               baos.write(67);
           } catch (java.io.IOException e) {
               e.printStackTrace();
           }
           byte[] result = baos.toByteArray();
           for (byte b : result) {
               System.out.print((char) b);
           }
       }
   }
   ```
   - 在这个示例中,我们使用`ByteArrayOutputStream`向字节数组中写入字符'A'、'B'、'C'(对应的ASCII码值),然后获取生成的字节数组并打印出对应的字符。

## 四、管道相关的节点流

### 1. `PipedInputStream`和`PipedOutputStream`
 - **功能与特点**
   - `PipedInputStream`和`PipedOutputStream`是用于线程间通信的字节流。`PipedOutputStream`用于向管道写入字节数据,`PipedInputStream`用于从管道读取字节数据。它们必须成对使用,并且在使用前需要连接起来。
   - 例如,在一个多线程的应用中,一个线程负责生成数据,另一个线程负责处理数据,就可以使用`PipedInputStream`和`PipedOutputStream`来实现数据在两个线程之间的传递。
 - **示例代码**
   ```java
   import java.io.IOException;
   import java.io.PipedInputStream;
   import java.io.PipedOutputStream;

   class DataProducer implements Runnable {
       private PipedOutputStream pos;

       public DataProducer(PipedOutputStream pos) {
           this.pos = pos;
       }

       @Override
       public void run() {
           try {
               String data = "Hello, Pipe!";
               byte[] byteData = data.getBytes();
               pos.write(byteData);
               pos.close();
           } catch (IOException e) {
               e.printStackTrace();
           }
       }
   }

   class DataConsumer implements Runnable {
       private PipedInputStream pis;

       public DataConsumer(PipedInputStream pis) {
           this.pis = pis;
       }

       @Override
       public void run() {
           try {
               int data;
               while ((data = pis.read())!= -1) {
                   System.out.print((char) data);
               }
               pis.close();
           } catch (IOException e) {
               e.printStackTrace();
           }
       }
   }

   public class PipedStreamExample {
       public static void main(String[] args) {
           try {
               PipedOutputStream pos = new PipedOutputStream();
               PipedInputStream pis = new PipedInputStream();
               pos.connect(pis);

               Thread producerThread = new Thread(new DataProducer(pos));
               Thread consumerThread = new Thread(new DataConsumer(pis));

               producerThread.start();
               consumerThread.start();

               producerThread.join();
               consumerThread.join();
           } catch (IOException | InterruptedException e) {
               e.printStackTrace();
           }
       }
   }
   ```
   - 在这个示例中,`DataProducer`线程使用`PipedOutputStream`向管道写入数据,`DataConsumer`线程使用`PipedInputStream`从管道读取数据。

节点流在Java的I/O系统中提供了直接与各种数据源和数据目的地交互的能力,是构建复杂I/O操作的基础组件。通过合理地使用不同类型的节点流,我们可以有效地处理文件、内存数据以及线程间的数据传输等各种I/O相关的任务。

上一篇:吴恩达深度学习笔记:卷积神经网络(Foundations of Convolutional Neural Networks)3.9-3.10


下一篇:采样频率 Fs 的一半(即奈奎斯特频率)