zl程序教程

您现在的位置是:首页 >  后端

当前栏目

从入门到精通:深入掌握 Java I/O、NIO 和其他流技术

JAVAnio技术入门 深入 掌握 精通 其他
2023-09-14 09:04:56 时间

在这里插入图片描述

🏅 欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!

Java I/O 和 NIO 是 Java 中常用的流技术,在文件操作、网络编程、序列化等方面都有重要作用。本文将为您讲解 Java I/O、NIO、以及一些其他流的基本概念、用法、代码示例和区别。

目录

一、Java I/O

Java I/O (Input/Output) 是对传统 I/O 操作的封装,它是以流的形式来操作数据的。在 I/O 操作中,数据被视为一系列按顺序排列的字节流。在 Java 中,这种字节流被称为 InputStream 和 OutputStream。

在这里插入图片描述

1.1 InputStream

InputStream 代表一个输入流,它是一个抽象类,不能被实例化。InputStream 定义了一些通用方法,如 read() 和 skip() 等,用于从输入流中读取数据。常用的 InputStream 实现类包括:

1.1.1 FileInputStream的代码示例

下面是使用 FileInputStream 读取文件内容的示例代码

import java.io.*;

public class FileInputStreamExample {

    public static void main(String[] args) {
        // 要读取的文件路径和名称
        String filePath = "C:/example/file.txt";
        // 创建输入流对象
        FileInputStream fis = null;

        try {
            fis = new FileInputStream(filePath);
            byte[] buffer = new byte[1024];
            int len;
            // 使用 while 循环读取文件,每次最多读取 1024 个字节
            while ((len = fis.read(buffer)) != -1) {
                // 将读取的字节转换为字符串,并输出到控制台
                String content = new String(buffer, 0, len, "UTF-8");
                System.out.println(content);
            }
        } catch (FileNotFoundException e) {
            System.out.println("File not found: " + filePath);
        } catch (IOException e) {
            System.out.println("Error reading file: " + e.getMessage());
        } finally {
            // 关闭输入流
            try {
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                System.out.println("Error closing file: " + e.getMessage());
            }
        }
    }
}

示例代码说明:

  • 在示例中,我们首先指定要读取的文件路径和名称。在实际使用中,您应该将其替换为实际的文件路径和名称。
  • 然后,我们使用文件路径创建一个FileInputStream对象。注意,在创建FileInputStream对象时,我们必须提供要读取的文件的路径和名称。
  • 接着,我们使用while循环从FileInputStream对象中读取数据。每次循环中,我们使用read()方法读取最多1024字节,并将读取的字节存储在一个缓冲区中。然后,我们将读取的字节转换为字符串,并在控制台上输出。在while循环结束后,我们关闭FileInputStream对象。
  • 注意:在使用FileInputStream类时,我们需要确保文件存在,并且我们有读取文件的权限。此外,在实际应用中,可能需要根据需要使用更高效的方法读取大型文件,以避免IO开销的问题。

1.1.2 ByteArrayInputStream的代码示例

下面是使用 ByteArrayInputStream 读取字节数组内容的示例代码

import java.io.*;

public class ByteArrayInputStreamExample {

    public static void main(String[] args) {
        byte[] bytes = { 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33 };
        // 创建字节输入流对象
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);

        try {
            byte[] buffer = new byte[1024];
            int len;
            // 使用 while 循环读取字节数组中的内容,每次最多读取 1024 个字节
            while ((len = bais.read(buffer)) != -1) {
                // 将读取的字节转换为字符串,并输出到控制台
                String content = new String(buffer, 0, len, "UTF-8");
                System.out.println(content);
            }
        } catch (IOException e) {
            System.out.println("Error reading byte array: " + e.getMessage());
        } finally {
            // 关闭输入流
            try {
                if (bais != null) {
                    bais.close();
                }
            } catch (IOException e) {
                System.out.println("Error closing byte array input stream: " + e.getMessage());
            }
        }
    }
}

示例代码说明:

  • 在示例中,我们首先创建了一个字节数组,其中包含ASCII码为72, 101, 108, 108, 111, 32, 87, 111,
    114, 108, 100, 33的字符序列"Hello World!"。
  • 然后,我们使用字节数组创建了一个ByteArrayInputStream对象。注意,在创建ByteArrayInputStream对象时,我们必须提供要从中读取的字节数组。
  • 接着,我们使用while循环从ByteArrayInputStream对象中读取数据。每次循环中,我们使用read()方法读取最多1024字节,并将读取的字节存储在一个缓冲区中。然后,我们将读取的字节转换为字符串,并在控制台上输出。在while循环结束后,我们关闭ByteArrayInputStream对象。
  • 注意:在使用ByteArrayInputStream类时,字节数组是一次性全部加载到内存中的,如果字节数组较大,可能会导致内存不足的问题。此外,在实际应用中,可能需要使用更高效的数据源(如文件或网络流)来存储数据,以避免内存限制的问题。

1.1.3 PipedInputStream的代码示例

PipedInputStream:管道输入流,用于线程之间的通信。

import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

public class PipedInputStreamExample {

    public static void main(String[] args) throws Exception {
        
        // 创建一对PipedInputStream和PipedOutputStream
        PipedInputStream input = new PipedInputStream();
        PipedOutputStream output = new PipedOutputStream(input);

        // 创建一个写线程
        Thread writerThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // 写入一些数据到PipedOutputStream
                    output.write("Hello, World!".getBytes());
                    output.close(); // 关闭PipedOutputStream
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });

        // 创建一个读线程
        Thread readerThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // 读取PipedInputStream中的数据
                    int data;
                    while ((data = input.read()) != -1) {
                        System.out.print((char) data); // 将数据打印到控制台
                    }
                    input.close(); // 关闭PipedInputStream
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });

        // 启动写线程和读线程
        writerThread.start();
        readerThread.start();

        // 等待写线程和读线程完成
        writerThread.join();
        readerThread.join();
    }
}

代码说明
PipedInputStream和PipedOutputStream是Java IO库提供的一对管道流,可以用于数据的发送和接收。
在示例中,我们首先创建了一个PipedInputStream和一个PipedOutputStream。它们被连接在一起,使得我们可以像使用一个普通的输入流和输出流一样进行读写操作。
接着,我们创建一个写线程来向管道中写入数据。在这个例子中,我们写入了字符串"Hello, World!"。
然后,我们创建一个读线程来从管道中读取数据。我们使用了一个while循环来读取数据,直到遇到了流的末尾。
最后,我们启动写线程和读线程,等待它们完成,然后关闭流。
注意:如果读线程在数据被写入管道之前就开始读取流,它将会阻塞(即等待数据被写入)。
另外,还要注意线程之间的同步问题。在这个例子中,我们使用了一个简单的join()方法来等待写线程和读线程完成。在实际使用中,您可能需要使用更高级的同步机制来确保线程之间的正确协作。

1.2 OutputStream

OutputStream 代表一个输出流,它也是一个抽象类,不能被实例化。OutputStream 定义了一些通用方法,如 write() 和 flush() 等,用于向输出流中写入数据。常用的 OutputStream 实现类包括:

1.2.1 FileOutputStream(文件输出流,用于向文件中写入数据)代码示例:

import java.io.*;

public class FileOutputStreamExample {

    public static void main(String[] args) {
        // 要写入的文件路径和名称
        String filePath = "C:/example/output.txt";
        // 要写入文件的内容
        String content = "Hello, World!";
        // 创建输出流对象
        FileOutputStream fos = null;

        try {
            fos = new FileOutputStream(filePath);
            // 将字符串转换为字节数组,并将其写入文件
            fos.write(content.getBytes("UTF-8"));
            // 刷新输出流
            fos.flush();
            // 输出提示信息
            System.out.println("Content has been written to " + filePath);
        } catch (FileNotFoundException e) {
            System.out.println("File not found: " + filePath);
        } catch (IOException e) {
            System.out.println("Error writing file: " + e.getMessage());
        } finally {
            // 关闭输出流
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                System.out.println("Error closing file: " + e.getMessage());
            }
        }
    }
}

示例代码说明:

  • 在示例中,我们首先指定要写入的文件路径和名称。在实际使用中,您应该将其替换为实际的文件路径和名称。
  • 然后,我们创建一个用于写入文件的输出流对象。在创建FileOutputStream对象时,如果文件不存在,Java将会自动创建它。
  • 接着,我们将要写入的内容转换为字节数组,并使用write()方法将其写入文件。
  • 然后,我们使用flush()方法刷新输出流。在文件操作完成后,我们应该始终调用flush()方法以确保所有数据都被写入到磁盘上的文件中。
  • 最后,我们关闭FileOutputStream对象,即使在发生异常时也应该关闭。
  • 注意:在使用FileOutputStream类时,我们需要确保文件存在,并且我们有写入文件的权限。此外,在实际应用中,可能需要使用更高效的方法来写入大型文件,以避免IO开销的问题。

1.2.2 ByteArrayOutputStream(字节数组输出流,用于将数据写入内存中的字节数组中)代码示例:

import java.io.*;

public class ByteArrayOutputStreamExample {

    public static void main(String[] args) {
        // 创建字节数组输出流对象
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        try {
            // 将字符串转换为字节数组,并写入到字节数组输出流中
            baos.write("Hello, World!".getBytes("UTF-8"));
            // 将字节数组输出流中的数据转换为字节数组
            byte[] bytes = baos.toByteArray();
            // 将字节数组转换为字符串,并输出到控制台
            String content = new String(bytes, "UTF-8");
            System.out.println(content);
        } catch (IOException e) {
            System.out.println("Error writing to byte array: " + e.getMessage());
        } finally {
            // 关闭字节数组输出流
            try {
                if (baos != null) {
                    baos.close();
                }
            } catch (IOException e) {
                System.out.println("Error closing byte array output stream: " + e.getMessage());
            }
        }
    }
}

示例代码说明:

  • 在示例中,我们首先创建了一个ByteArrayOutputStream对象,用于向内存中的字节数组中写入数据。
  • 然后,我们将要写入的内容转换为字节数组,并使用write()方法将其写入ByteArrayOutputStream对象。
  • 接着,我们调用toByteArray()方法将ByteArrayOutputStream对象中的数据转换为字节数组。需要注意的是,要在调用toByteArray()方法之前先关闭ByteArrayOutputStream
  • 最后,我们将字节数组转换为字符串,并将其输出到控制台。
  • 注意:在使用ByteArrayOutputStream类时,需要注意内存占用问题。BytesArrayOutputStream类主要用于在内存中临时存储数据。对于大数据,可能需要使用其他方式存储。

1.2.3 PipedOutputStream(管道输出流,用于线程之间的通信)代码示例:

import java.io.*;

public class PipedOutputStreamExample {

    public static void main(String[] args) {
        // 创建一对PipedInputStream和PipedOutputStream
        PipedInputStream input = new PipedInputStream();
        PipedOutputStream output = new PipedOutputStream();

        try {
            // 将输入流和输出流连接起来
            input.connect(output);

            // 创建一个写线程
            Thread writerThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        // 写入一些数据到PipedOutputStream
                        output.write("Hello, World!".getBytes("UTF-8"));
                        // 刷新PipedOutputStream
                        output.flush();
                        // 关闭PipedOutputStream
                        output.close();
                    } catch (IOException e) {
                        System.out.println("Error writing to pipe: " + e.getMessage());
                    }
                }
            });

            // 创建一个读线程
            Thread readerThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        // 读取PipedInputStream中的数据
                        byte[] buffer = new byte[1024];
                        int len = input.read(buffer);
                        // 将读取的字节转换为字符串,并输出到控制台
                        String content = new String(buffer, 0, len, "UTF-8");
                        System.out.println(content);
                        // 关闭PipedInputStream
                        input.close();
                    } catch (IOException e) {
                        System.out.println("Error reading from pipe: " + e.getMessage());
                    }
                }
            });

            // 启动写线程和读线程
            writerThread.start();
            readerThread.start();

            // 等待写线程和读线程完成
            writerThread.join();
            readerThread.join();
        } catch (IOException | InterruptedException e) {
            System.out.println("Error communicating between threads: " + e.getMessage());
        }
    }
}

示例代码说明:

  • 在示例中,我们首先创建了一对PipedInputStreamPipedOutputStream,用于在线程之间进行通信。
  • 接着,我们使用connect()方法将PipedInputStreamPipedOutputStream连接起来。
  • 然后,我们创建一个写线程和一个读线程。在写线程中,我们向PipedOutputStream写入数据,并使用flush()和close()方法刷新和关闭输出流。在读线程中,我们从PipedInputStream读取数据,并将其转换为字符串并打印到控制台。在读操作完成后,我们关闭输入流
  • 最后,我们启动写线程读线程,并等待它们完成。
  • 注意:在使用PipedInputStreamPipedOutputStream类时,需要考虑线程同步问题,以确保在线程之间正确地交换数据。在实际应用中,您可能需要使用更高级的同步机制来确保线程之间的协作

1.3 Reader 和 Writer

除了字节流,Java 还提供字符流,字符流类似于字节流,不同之处在于字符流是按字符读写数据,而不是按字节。Java 中最基本的字符流是 Reader 和 Writer,它们是基于 InputStream 和 OutputStream 的转换类,用于完成字节流与字符流之间的转换。

1.3.1 Reader:字符输入流。

常用的实现类包括 FileReader 和 InputStreamReader等

1.3.2 FileReader 代码示例:

import java.io.FileReader;  // 引入 FileReader 类
import java.io.IOException; // 引入 IOException 类

public class FileReaderExample {
    public static void main(String[] args) {
        // 定义文件路径
        String filePath = "example.txt";

        try {
            // 创建 FileReader 对象
            FileReader fileReader = new FileReader(filePath);

            // 读取字符
            int character;
            while ((character = fileReader.read()) != -1) {
                // 打印字符
                System.out.print((char) character);
            }

            // 关闭 FileReader 对象
            fileReader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

示例代码说明:

  • 第9行中定义了要读取的文件的路径。
  • 在第13-24行中,我们使用try-catch块来打开文件并读取文件中的字符。在try块中,我们创建一个FileReader对象(fileReader)并传入文件路径。用while循环逐个字符读取,在遇到文件末尾时返回EOF(-1)。我们将每个字符强制转换为字符并使用System.out.print()打印。
  • 最后,在第29行中关闭FileReader对象以释放资源。如果在读取文件时发生错误,则通过捕获IOException异常并打印堆栈跟踪来处理该异常。

1.3.3 InputStreamReader 代码示例:

import java.io.BufferedReader; // 引入 BufferedReader 类
import java.io.IOException;    // 引入 IOException 类
import java.io.InputStreamReader; // 引入 InputStreamReader 类

public class InputStreamReaderExample {
    public static void main(String[] args) {

        try {
            // 创建 InputStreamReader 对象
            InputStreamReader inputStreamReader = new InputStreamReader(System.in);

            // 创建 BufferedReader 对象
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

            // 获取用户输入
            System.out.println("请输入字符串:");
            String inputString = bufferedReader.readLine();

            // 打印用户输入
            System.out.println("您输入的字符串是:" + inputString);

            // 关闭 BufferedReader 对象
            bufferedReader.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

示例代码说明:

  • 第2行:引入 BufferedReader 类,使代码能够访问该类的功能。
  • 第3行:引入 IOException 类,用于处理读取错误。
  • 第4行:引入 InputStreamReader 类,使用 InputStreamReader 将字节流转换为字符流。
  • 第7-20行:在 try-catch 块中,创建 InputStreamReaderBufferedReader对象,获取用户输入并将其打印到控制台。
  • 第9行:创建 InputStreamReader 对象,将标准输入(System.in)作为参数传递给它。
  • 第12行:创建 BufferedReader 对象,将 InputStreamReader 对象作为参数传递给它。
  • 第15行:使用 BufferedReader 的 readLine() 方法获取用户输入,直到用户按下回车键。
  • 第18行:打印用户输入。
  • 第21行:关闭 BufferedReader 对象以释放资源。
  • 第22行:捕获任何可能出现的 IOException 异常,并打印堆栈跟踪以进行调试和故障排除。

1.4 Writer:字符输出流

1.4.1 FileWriter代码示例:

import java.io.*;

public class FileWriterExample {
    public static void main(String[] args) {
        FileWriter writer = null;

        try {
            writer = new FileWriter("example.txt");
            writer.write("Hello World!");
            writer.close();
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        } finally {
            try {
                if (writer != null)
                    writer.close();
            } catch (IOException e) {
                System.out.println("Error: " + e.getMessage());
            }
        }
    }
}

示例代码说明:

  • 在这个例子中,创建了一个FileWriter对象 writer,并将字符串"Hello World!"写入文件
    “example.txt”。然后,我们使用close()方法关闭写入器以确保所有的数据都被刷新到磁盘。
  • 注意:在使用FileWriter时,要确保在不再需要它时关闭它以确保所有的字符都被刷新到文件中。如果您使用Java7或更高版本,可以考虑使用try-with-resources语句,这样您就不需要显式地关闭写入器。

1.4.2 OutputStreamWriter代码示例:

import java.io.*;

public class OutputStreamWriterExample {
    public static void main(String[] args) {
        FileOutputStream outputStream = null;
        OutputStreamWriter writer = null;

        try {
            outputStream = new FileOutputStream("example.txt");
            writer = new OutputStreamWriter(outputStream, "UTF-8");
            writer.write("Hello World!");
            writer.close();
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        } finally {
            try {
                if (writer != null)
                    writer.close();
                if (outputStream != null)
                    outputStream.close();
            } catch (IOException e) {
                System.out.println("Error: " + e.getMessage());
            }
        }
    }
}

示例代码说明:

  • 在这个例子中,创建了一个FileOutputStream对象 outputStream 用于写入文件
    “example.txt”。接着,创建了一个OutputStreamWriter对象 writer,它被用于将字符串"Hello
    World!"写入到文件中。指定了"UTF-8"作为字符编码。
  • 最后,使用close()方法关闭写入器和输出流以确保所有的数据都被刷新到磁盘上。
  • 注意:在使用OutputStreamWriter时,要确保在不再需要它时关闭它以确保所有的字符都被刷新到文件中。如果您使用Java 7或更高版本,可以考虑使用try-with-resources语句,这样您就不需要显式地关闭写入器和输出流。

1.5 BufferedInputStre加粗样式am 和 BufferedOutputStream

BufferedInputStream 和 BufferedOutputStream 是 I/O 包中提供的缓冲输入输出流。它们可以提高 I/O 操作的效率,具有较好的缓存机制,能够减少磁盘操作,缩短文件传输时间。使用 BufferedInputStream 和 BufferedOutputStream 进行读取和写入时,Java 会自动调整缓冲区的大小,使其能够适应不同的数据传输速度。

1.5.1 BufferedInputStream 和 BufferedOutputStream代码示例

import java.io.BufferedInputStream;   // 引入 BufferedInputStream 类
import java.io.BufferedOutputStream;  // 引入 BufferedOutputStream 类
import java.io.FileInputStream;        // 引入 FileInputStream 类
import java.io.FileOutputStream;       // 引入 FileOutputStream 类
import java.io.IOException;            // 引入 IOException 类

public class BufferedStreamsExample {
    public static void main(String[] args) {

        String sourceFile = "source.txt";
        String targetFile = "target.txt";

        try {
            // 创建 BufferedInputStream 和 BufferedOutputStream 对象
            BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(sourceFile));
            BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(targetFile));

            // 读取数据,直到读取的内容为-1
            int data;
            while ((data = bufferedInputStream.read()) != -1) {
                bufferedOutputStream.write(data);
            }

            // 关闭 BufferedInputStream 和 BufferedOutputStream 对象
            bufferedInputStream.close();
            bufferedOutputStream.close();

            // 打印成功信息
            System.out.println("复制文件成功!");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

示例代码说明:

  • 第2行:引入 BufferedInputStream 类,使代码能够访问该类的功能。
  • 第3行:引入 BufferedOutputStream 类,使用 BufferedInputStream
    BufferedOutputStream 可以一次读取更多数据并将其存储在缓冲区中,从而提高I/O性能。
  • 第4行:引入 FileInputStream 类,用于文件输入流。
  • 第5行:引入 FileOutputStream 类,用于文件输出流。
  • 第6行:引入 IOException 类,用于处理输入/输出错误。
  • 第9-10行:定义源文件和目标文件路径。
  • 第12-21行:在 try-catch 块中,打开源文件和目标文件并使用 BufferedInputStream
    BufferedOutputStream 对象复制文件。
  • 第14行:创建 BufferedInputStream 对象并传入源文件路径。
  • 第15行:创建 BufferedOutputStream 对象并传入目标文件路径。
  • 第18-19行:使用 while 循环读取源文件中的内容并将其写入目标文件中。
  • 第22-23行:关闭 BufferedInputStreamBufferedOutputStream 对象以释放资源。
  • 第26行:打印成功信息。
  • 第27行:捕获任何可能出现的 IOException 异常,并打印堆栈跟踪以进行调试和故障排除。

1.6 ObjectInputStream 和 ObjectOutputStream

ObjectInputStream 和 ObjectOutputStream 是用于对象序列化和反序列化的类,使用 ObjectInputStream 和 ObjectOutputStream 可以将 Java 对象转换为字节流进行传输或存储。在网络传输和文件存储中,ObjectInputStream 和 ObjectOutputStream 通常会被使用到。

1.6.1 ObjectInputStream 和 ObjectOutputStream代码示例

import java.io.FileInputStream;          // 引入 FileInputStream 类
import java.io.FileOutputStream;         // 引入 FileOutputStream 类
import java.io.ObjectInputStream;        // 引入 ObjectInputStream 类
import java.io.ObjectOutputStream;       // 引入 ObjectOutputStream 类
import java.io.Serializable;             // 引入 Serializable 接口

class Person implements Serializable {
    String name;
    int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String toString() {
        return "姓名:" + name + "\n年龄:" + age;
    }
}

public class ObjectStreamsExample {
    public static void main(String[] args) {

        String filePath = "person.dat";

        // 创建 Person 对象
        Person person = new Person("Alice", 20);

        try {
            // 创建 ObjectOutputStream 对象
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(filePath));

            // 将 Person 对象写入文件
            objectOutputStream.writeObject(person);

            // 关闭 ObjectOutputStream 对象
            objectOutputStream.close();

            // 创建 ObjectInputStream 对象
            ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filePath));

            // 从文件中读取 Person 对象
            Person personFromFile = (Person) objectInputStream.readObject();

            // 关闭 ObjectInputStream 对象
            objectInputStream.close();

            // 打印读取的对象
            System.out.println(personFromFile);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

示例代码说明:

  • 第2行:引入 FileInputStream 类,使代码能够访问该类的功能。
  • 第3行:引入 FileOutputStream 类,用于文件输出流。
  • 第4行:引入 ObjectInputStream 类,用于对象读取流。
  • 第5行:引入 ObjectOutputStream 类,用于对象写入流。
  • 第6行:引入 Serializable 接口以使 Person 对象可序列化。
  • 第8-17行:在 Person 类中,创建一个简单的 POJO(Plain Old Java Object)类并实现
    Serializable 接口。
  • 第19-39行:在 main 方法中,创建一个 Person 对象,并使用 ObjectOutputStream 将该对象写入文件。然后使用 ObjectInputStream 从文件中读取 Person 对象,并将其打印到控制台。
  • 第22-23行:定义要写入和读取的文件路径。
  • 第25-30行:创建一个 Person 对象并将其写入文件中。
  • 第32行:关闭 ObjectOutputStream 对象以释放资源。
  • 第34-39行:创建一个 ObjectInputStream 对象并从文件中读取 Person
    对象。然后关闭ObjectInputStream 对象以释放资源。
  • 第41行:打印从文件中读取的 Person 对象。
  • 第42行:捕获任何可能出现的异常,并打印堆栈跟踪以进行调试和故障排除。

二、Java NIO

Java NIO (New I/O) 是 Java 1.4 引入的,在 Java 7 中又进行了一些增强。NIO 可以提高 I/O 操作的效率,它的核心是通道 (Channel) 和缓冲区 (Buffer)。
在这里插入图片描述

2.1 Channel

Channel 是一种新的 I/O 抽象,它与传统的 InputStream 和 OutputStream 不同,Channel 可以同时进行读和写操作,而且可以对其进行更细粒度的控制。Java NIO 中最基本的 Channel 包括:

2.1.1 FileChannel(用于文件的读写操作)代码示例:

使用FileChannel从源文件中读取内容并将其写入到目标文件。

import java.io.FileInputStream;          // 引入 FileInputStream 类
import java.io.FileOutputStream;         // 引入 FileOutputStream 类
import java.nio.ByteBuffer;              // 引入 ByteBuffer 类
import java.nio.channels.FileChannel;    // 引入 FileChannel 类

public class FileChannelExample {
    public static void main(String[] args) {

        String sourceFile = "source.txt";
        String targetFile = "target.txt";

        try {
            // 使用 FileInputStream 和 FileOutputStream 打开源文件和目标文件
            FileInputStream fileInputStream = new FileInputStream(sourceFile);
            FileOutputStream fileOutputStream = new FileOutputStream(targetFile);

            // 获取 FileChannel 对象
            FileChannel sourceChannel = fileInputStream.getChannel();
            FileChannel targetChannel = fileOutputStream.getChannel();

            // 创建 ByteBuffer 对象
            ByteBuffer buffer = ByteBuffer.allocate(1024);

            // 从源文件中读取内容并将其写入目标文件
            while (sourceChannel.read(buffer) != -1) {
                buffer.flip();  // 准备写入(flip buffer)
                targetChannel.write(buffer);  // 向目标文件写入数据
                buffer.clear(); // 缓冲区清空(clear buffer)
            }

            // 关闭所有的 FileChannel、FileInputStream 和 FileOutputStream 对象
            sourceChannel.close();
            targetChannel.close();
            fileInputStream.close();
            fileOutputStream.close();

            // 打印成功信息
            System.out.println("文件复制成功!");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

示例代码说明:

  • 第2行:引入 FileInputStream 类,使代码能够访问该类的功能。
  • 第3行:引入 FileOutputStream 类,用于文件输出流。
  • 第4行:引入 ByteBuffer 类,可以用于处理二进制数据。
  • 第5行:引入 FileChannel 类,主要用于对文件进行读写操作。
  • 第8-9行:定义源文件和目标文件路径。
  • 第11-27行:在 try-catch 块中,打开源文件和目标文件,并使用 FileChannel
    对象从源文件中读取内容并将其写入到目标文件中。
  • 第13-14行:使用 FileInputStreamFileOutputStream 打开源文件和目标文件。
  • 第16-17行:使用 sourceChanneltargetChannel 变量获取源文件和目标文件的 FileChannel
    对象。
  • 第19行:使用 ByteBuffer.allocate() 创建一个 ByteBuffer 对象,它可以存储 1024 个字节大小的数据。
  • 第22-25行:使用 while 循环从源文件中读取数据,然后使用 targetChannel 对象将其写入到目标文件中。使用flip() 函数切换缓冲区以允许写入。使用 clear() 函数清空缓冲区以便下一次读取。
  • 第28-31行:关闭所有 FileChannelFileInputStreamFileOutputStream 对象以释放资源。
  • 第33行:打印成功信息。
  • 第34行:在 catch 块中捕获任何可能出现的异常,并打印堆栈跟踪以进行调试和故障排除。

2.1.2 DatagramChannel(用于 UDP 协议的数据读写操作)代码示例:

使用DatagramChannel从一个端口读取数据并将数据发送到另一个端口。

import java.io.IOException;                    // 引入 IOException 类
import java.net.InetSocketAddress;             // 引入 InetSocketAddress 类
import java.nio.ByteBuffer;                     // 引入 ByteBuffer 类
import java.nio.channels.DatagramChannel;       // 引入 DatagramChannel 类

public class DatagramChannelExample {
    public static void main(String[] args) {
        int receivePort = 8888;
        int sendPort = 9999;

        try {
            // 创建 DatagramChannel 对象
            DatagramChannel receiveChannel = DatagramChannel.open();

            // 绑定接收端口
            receiveChannel.socket().bind(new InetSocketAddress(receivePort));
            System.out.println("接收端口 " + receivePort + " 正在等待数据...");

            // 创建数据缓冲区对象
            ByteBuffer receiveBuffer = ByteBuffer.allocate(1024);

            // 从 receiveChannel 接收数据
            receiveChannel.receive(receiveBuffer);

            // 显示收到的数据
            System.out.println("收到的数据是:" + new String(receiveBuffer.array()));

            // 关闭 receiveChannel 对象
            receiveChannel.close();

            // 创建 DatagramChannel 对象
            DatagramChannel sendChannel = DatagramChannel.open();

            // 创建数据缓冲区对象
            ByteBuffer sendBuffer = ByteBuffer.allocate(1024);

            // 向数据缓冲区写入数据
            sendBuffer.clear();
            sendBuffer.put("Hello World".getBytes());
            sendBuffer.flip();

            // 发送数据到指定端口
            sendChannel.send(sendBuffer, new InetSocketAddress("localhost", sendPort));
            System.out.println("数据已发送到端口 " + sendPort);

            // 关闭 sendChannel 对象
            sendChannel.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

示例代码说明:

  • 第2行:引入 IOException 类,用于处理输入/输出错误。
  • 第3行:引入 InetSocketAddress 类,它表示 IP 地址和端口号。
  • 第4行:引入 ByteBuffer 类,可以用于处理二进制数据。
  • 第5行:引入 DatagramChannel 类,用于 UDP 协议的数据读写操作。
  • 第8-9行:定义本地接收端口和发送端口。
  • 第11-32行:在 try-catch 块中,使用 DatagramChannel 接收数据和发送数据。首先创建
    DatagramChannel 对象并绑定接收端口。然后创建 ByteBuffer 对象并使用 receive()
    方法从通道中接收数据。接收到数据后,从缓冲区中读取数据并打印。然后关闭 receiveChannel 对象。接下来创建DatagramChannel 对象并使用指定的套接字地址和端

2.1.3 SocketChannel 和 ServerSocketChannel(用于 TCP 协议的数据读写操作)代码示例:

下面是一个简单的示例,演示如何使用 SocketChannel 和 ServerSocketChannel 进行基本的 TCP 数据读写操作。

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

public class TCPExample {
    public static void main(String[] args) throws Exception {
        // 创建 ServerSocketChannel 并绑定端口
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress(8888));
        serverSocketChannel.configureBlocking(false);

        // 创建一个 ByteBuffer 用于接收数据
        ByteBuffer buf = ByteBuffer.allocate(1024);

        // 等待客户端连接
        while (true) {
            SocketChannel socketChannel = serverSocketChannel.accept();
            if (socketChannel != null) {
                // 客户端已连接,从 SocketChannel 中读取数据
                int bytesRead = socketChannel.read(buf);
                while (bytesRead != -1) {
                    // 处理读取到的数据
                    System.out.println(new String(buf.array(), 0, bytesRead));
                    
                    // 清空 ByteBuffer,进行下一次读取
                    buf.clear();
                    bytesRead = socketChannel.read(buf);
                }
            }
        }
    }
}

示例代码说明:

  1. 创建一个 ServerSocketChannel 并绑定到本地端口 8888,然后将其设置为非阻塞模式。
  2. 创建一个 ByteBuffer 用于接收数据。
  3. 进入一个死循环,不断等待客户端连接。
  4. 当客户端连接时,从 SocketChannel 中读取数据,并将读取到的数据打印到控制台。
  5. 清空 ByteBuffer,进行下一次读取。

需要注意的点:

  • 在代码中每次读取结束都需要清空 ByteBuffer,否则其 position 属性不会自动归零,可能导致数据读取不正确。
  • 由于使用非阻塞模式,如果调用了 accept() 方法但没有立即接收到客户端连接,该方法会返回 null,需要继续循环等待。
  • 本代码只演示了从客户端读取数据的部分,如果需要向客户端发送数据需要调用****SocketChannel**.write()**方法

如果想要向客户端发送数据,可以使用以下代码:

// 创建一个 ByteBuffer 用于发送数据
ByteBuffer buf = ByteBuffer.wrap("Hello, world!".getBytes());

// 向客户端发送数据
socketChannel.write(buf);

2.2 Buffer

Buffer 是一个对象,它包含一些要写入或要读出的数据。在 NIO 中,Buffer 可以被看作为一个字节数组,但是它的读取和写入操作比直接的字节数组更加高效。NIO 中最常用的 Buffer 类型包括:

2.2.1 ByteBuffer(字节缓冲区,最常用的缓冲区类型,用于对字节数据的读写操作)示例代码:

import java.nio.ByteBuffer;

public class ByteBufferExample {
  public static void main(String[] args) {
    // 创建一个新的字节缓冲区,初始容量为10个字节
    ByteBuffer buffer = ByteBuffer.allocate(10);
    
    // 向缓冲区中写入4个字节
    buffer.put((byte) 1);
    buffer.put((byte) 2);
    buffer.put((byte) 3);
    buffer.put((byte) 4);
    
    // 输出缓冲区中的内容
    buffer.flip(); // 将缓冲区切换成读模式
    System.out.println(buffer.get()); // 输出1
    System.out.println(buffer.get()); // 输出2
    System.out.println(buffer.get()); // 输出3
    System.out.println(buffer.get()); // 输出4
    
    // 将缓冲区清空并重新写入数据
    buffer.clear();
    buffer.put((byte) 5);
    buffer.put((byte) 6);
    buffer.put((byte) 7);
    buffer.put((byte) 8);
    
    // 输出缓冲区中的内容,方法同上
    buffer.flip();
    System.out.println(buffer.get()); // 输出5
    System.out.println(buffer.get()); // 输出6
    System.out.println(buffer.get()); // 输出7
    System.out.println(buffer.get()); // 输出8
  }
}

示例代码说明:

  • 在上面的示例中,我们使用ByteBuffer类的**allocate()**方法创建了一个新的字节缓冲区,然后向缓冲区中写入4个字节的数据。
  • 接着,我们通过调用**flip()方法将缓冲区切换成读模式,并使用get()**方法读取缓冲区中的数据,并按顺序输出每个字节。
  • 最后,我们清空缓冲区并重新写入数据,再次将缓冲区切换成读模式,并使用**get()**方法读取缓冲区中的数据。

2.2.2 CharBuffer(字符缓冲区,用于对字符数据的读写操作)示例代码:

import java.nio.CharBuffer;

public class CharBufferExample {
    public static void main(String[] args) {
        // 创建一个新的字符缓冲区,初始容量为10个字符
        CharBuffer buffer = CharBuffer.allocate(10);
        
        // 向缓冲区中写入4个字符
        buffer.put('a');
        buffer.put('b');
        buffer.put('c');
        buffer.put('d');
        
        // 输出缓冲区中的内容
        buffer.flip(); // 将缓冲区切换成读模式
        System.out.println(buffer.get()); // 输出a
        System.out.println(buffer.get()); // 输出b
        System.out.println(buffer.get()); // 输出c
        System.out.println(buffer.get()); // 输出d
        
        // 将缓冲区清空并重新写入数据
        buffer.clear();
        buffer.put('e');
        buffer.put('f');
        buffer.put('g');
        buffer.put('h');
        
        // 输出缓冲区中的内容,方法同上
        buffer.flip();
        System.out.println(buffer.get()); // 输出e
        System.out.println(buffer.get()); // 输出f
        System.out.println(buffer.get()); // 输出g
        System.out.println(buffer.get()); // 输出h
    }
}

示例代码说明:

  • 在上面的示例中,我们使用CharBuffer类的**allocate()**方法创建了一个新的字符缓冲区,然后向缓冲区中写入4个字符的数据。
  • 接着,我们通过调用flip()方法将缓冲区切换成读模式,并使用get()方法读取缓冲区中的数据,并按顺序输出每个字符。
  • 最后,我们清空缓冲区并重新写入数据,再次将缓冲区切换成读模式,并使用**get()**方法读取缓冲区中的数据。

2.2.3 ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer 等示例代码:

import java.nio.*;

public class BasicBufferExample {
    public static void main(String[] args) {
        // 创建各种基本数据类型的缓冲区,初始容量为10
        ShortBuffer shortBuf = ShortBuffer.allocate(10);
        IntBuffer intBuf = IntBuffer.allocate(10);
        LongBuffer longBuf = LongBuffer.allocate(10);
        FloatBuffer floatBuf = FloatBuffer.allocate(10);
        DoubleBuffer doubleBuf = DoubleBuffer.allocate(10);
        
        // 向缓冲区中写入数据
        shortBuf.put((short) 1);
        intBuf.put(2);
        longBuf.put(3L);
        floatBuf.put(4.0f);
        doubleBuf.put(5.0);
        
        // 反转缓冲区,切换到读模式
        shortBuf.flip();
        intBuf.flip();
        longBuf.flip();
        floatBuf.flip();
        doubleBuf.flip();
        
        // 读取缓冲区中的数据
        System.out.println(shortBuf.get()); // 输出1
        System.out.println(intBuf.get()); // 输出2
        System.out.println(longBuf.get()); // 输出3
        System.out.println(floatBuf.get()); // 输出4.0
        System.out.println(doubleBuf.get()); // 输出5.0
    }
}

示例代码说明:

  • 在上面的示例中,我们分别创建了ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer等基本数据类型的缓冲区。
  • 接着,我们向这些缓冲区中写入了对应数据类型的数据。
  • 然后我们通过调用**flip()**方法,将缓冲区切换成读模式,并通过get()方法读取缓冲区中的数据,并按顺序输出每一个数据类型的内容。

2.3 Selector

Selector 是 Java NIO 类库中的一个重要组件,它用于监听多个 Channel 的事件。在一个线程中,通过 Selector 可以监听多个 Channel 的 IO 事件,并实现了基于事件响应的架构。Selector 可以让单个线程处理多个 Channel,因此它可以提高多路复用的效率。

2.3.1Selector让单线程处理多个Channel的代码示例:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

public class SelectorExample {
    public static void main(String[] args) throws IOException {
        // 创建一个ServerSocketChannel,监听本地端口
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress("localhost", 8080));
        serverSocketChannel.configureBlocking(false);
        
        // 创建一个Selector,并将serverSocketChannel注册到Selector上
        Selector selector = Selector.open();
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        System.out.println("Server started on port 8080");

        while (true) {
            // 如果没有任何事件发生,则阻塞等待
            selector.select();

            // 处理事件
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
            while (keyIterator.hasNext()) {
                SelectionKey key = keyIterator.next();

                if (key.isAcceptable()) {
                    // 处理新的连接请求
                    ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
                    SocketChannel clientChannel = serverChannel.accept();
                    clientChannel.configureBlocking(false);
                    System.out.println("Accepted connection from " + clientChannel.getRemoteAddress());
                    clientChannel.register(selector, SelectionKey.OP_READ);

                } else if (key.isReadable()) {
                    // 处理读事件
                    SocketChannel clientChannel = (SocketChannel) key.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    int bytesRead = clientChannel.read(buffer);
                    String message = new String(buffer.array(), 0, bytesRead);
                    System.out.println("Received message from " + clientChannel.getRemoteAddress() + ": " + message);

                    // 回写数据
                    ByteBuffer outputBuffer = ByteBuffer.wrap(("Echo: " + message).getBytes());
                    clientChannel.write(outputBuffer);
                }

                // 从待处理事件集合中移除当前事件
                keyIterator.remove();
            }
        }
    }
}

示例代码说明:

  • 使用ServerSocketChannel监听本地8080端口,并将ServerSocketChannel注册到Selector上。
  • while循环中,我们通过调用select()方法等待事件发生,如果有事件发生,则从Selector中获取待处理事件集合,然后遍历事件集合,处理每个事件。
  • 如果当前事件是新的连接请求,则接受该连接,并将对应的SocketChannel注册到Selector上,使用OP_READ模式表示可以读取数据。
  • 如果当前事件是可读的,则读取SocketChannel中的数据并进行回写,回写时使用ByteBuffer包装需要回写的数据,并将其写入到SocketChannel中。
  • 最后,我们从待处理事件集合中移除当前事件。

三、其他流

除了 Java I/O 和 NIO,Java 还提供了一些其他的流技术,包括:

3.1 ZipInputStream 和 ZipOutputStream

ZipInputStream 和 ZipOutputStream 可以用于处理 ZIP 文件格式,ZipInputStream 可以从 ZIP 文件中读取数据,ZipOutputStream 可以向 ZIP 文件中写入数据。

3.1.1 ZipInputStream 和 ZipOutputStream代码示例

3.1.1.1 ZipInputStream示例代码:

import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class ZipExample {
    public static void main(String[] args) throws IOException {
        // 输入文件路径和输出压缩文件路径
        String inputFile = "/path/to/input/file";
        String outputFile = "/path/to/output/file.zip";

        // 创建ZipOutputStream,并设置压缩级别
        ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(outputFile));
        zipOutputStream.setLevel(9);

        // 读取需要压缩的文件到文件输入流
        FileInputStream fileInputStream = new FileInputStream(inputFile);
        BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);

        // 设置压缩文件内部的名称
        ZipEntry zipEntry = new ZipEntry(inputFile);
        zipOutputStream.putNextEntry(zipEntry);

        // 写入压缩文件
        byte[] buf = new byte[1024];
        int len;
        while ((len = bufferedInputStream.read(buf)) > 0) {
            zipOutputStream.write(buf, 0, len);
        }

        bufferedInputStream.close();
        zipOutputStream.closeEntry();
        zipOutputStream.close();

        System.out.println("File compressed successfully");
    }
}

示例代码说明:

  • 首先,我们创建ZipOutputStream并设置压缩级别。
  • 接着,我们创建输入文件的FileInputStream,并使用BufferedInputStream包装它。
  • 我们接着设置压缩文件内部的名称,并使用****zipOutputStream**.putNextEntry()方法将其写入ZipOutputStream**中。
  • 最后,我们从缓冲区读取文件数据并将其写入ZipOutputStream中。最后关闭输入流和ZipOutputStream

3.1.1.2 ZipOutputStream示例代码:

import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public class UnzipExample {
    public static void main(String[] args) throws IOException {
        // 输入压缩文件路径和输出文件路径
        String inputFile = "/path/to/input/file.zip";
        String outputFile = "/path/to/output/file";

        // 创建ZipInputStream
        ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(inputFile));

        // 循环读取压缩文件中的条目
        ZipEntry zipEntry = zipInputStream.getNextEntry();
        while (zipEntry != null) {
            // 如果是目录,则创建空目录
            if (zipEntry.isDirectory()) {
                new File(outputFile + File.separator + zipEntry.getName()).mkdirs();

            } else { // 如果是文件,则输出文件
                FileOutputStream fileOutputStream = new FileOutputStream(outputFile + File.separator
                        + zipEntry.getName());
                byte[] buf = new byte[1024];
                int len;
                while ((len = zipInputStream.read(buf)) > 0) {
                    fileOutputStream.write(buf, 0, len);
                }
                fileOutputStream.close();
            }

            zipInputStream.closeEntry();
            zipEntry = zipInputStream.getNextEntry();
        }

        zipInputStream.close();

        System.out.println("File uncompressed successfully");
    }
}

示例代码说明:

  • 使用ZipInputStream从指定输入文件中解压文件到指定的输出文件夹中。
  • 我们创建ZipInputStream,然后循环读取压缩文件中的条目。如果当前条目是目录,则创建空目录,并使用mkdirs()方法创建目录。如果当前条目是文件,则使用FileOutputStream将文件写入到指定的输出文件中。
  • 最后关闭当前ZipEntry,并通过getNextEntry()方法获取ZipInputStream中的下一个条目。

3.2 GZIPInputStream 和 GZIPOutputStream

GZIPInputStream 和 GZIPOutputStream 可以用于进行 GZIP 压缩,GZIPInputStream 可以从压缩文件中读取数据,GZIPOutputStream 可以将数据写入压缩文件中。

3.2.1 GZIPInputStream 和 GZIPOutputStream代码示例

3.2.2 GZIPInputStream代码示例:

import java.io.*;
import java.util.zip.GZIPOutputStream;

public class GzipExample {
    public static void main(String[] args) throws IOException {
        // 输入文件路径和输出压缩文件路径
        String inputFile = "/path/to/input/file";
        String outputFile = "/path/to/output/file.gz";

        // 创建GZIPOutputStream,并设置压缩级别
        GZIPOutputStream gzipOutputStream = new GZIPOutputStream(new FileOutputStream(outputFile));
        gzipOutputStream.setLevel(9);

        // 读取需要压缩的文件到文件输入流
        FileInputStream fileInputStream = new FileInputStream(inputFile);
        BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);

        // 写入压缩文件
        byte[] buf = new byte[1024];
        int len;
        while ((len = bufferedInputStream.read(buf)) > 0) {
            gzipOutputStream.write(buf, 0, len);
        }

        bufferedInputStream.close();
        gzipOutputStream.close();

        System.out.println("File compressed successfully");
    }
}

示例代码说明:

  • 使用GZIPOutputStream将指定的输入文件压缩成输出文件。
  • 首先,创建GZIPOutputStream并设置压缩级别。
  • 接着,创建输入文件的FileInputStream,并使用BufferedInputStream包装它。
  • 接着从缓冲区读取文件数据并将其写入GZIPOutputStream中。最后关闭输入流和GZIPOutputStream

3.2.3 GZIPOutputStream代码示例:

import java.io.*;
import java.util.zip.GZIPInputStream;

public class GunzipExample {
    public static void main(String[] args) throws IOException {
        // 输入压缩文件路径和输出文件路径
        String inputFile = "/path/to/input/file.gz";
        String outputFile = "/path/to/output/file";

        // 创建GZIPInputStream
        GZIPInputStream gzipInputStream = new GZIPInputStream(new FileInputStream(inputFile));

        // 输出文件
        FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
        byte[] buf = new byte[1024];
        int len;
        while ((len = gzipInputStream.read(buf)) > 0) {
            fileOutputStream.write(buf, 0, len);
        }

        gzipInputStream.close();
        fileOutputStream.close();

        System.out.println("File uncompressed successfully");
    }
}

示例代码说明:

  • 使用GZIPInputStream从指定输入文件中解压文件到指定的输出文件中。
  • 首先,我们创建GZIPInputStream,然后从缓冲区读取文件数据并将其写入到指定的输出文件中。
  • 最后,我们关闭输入流和输出流。

3.3 ByteArrayInputStream 和 ByteArrayOutputStream

ByteArrayInputStream 和 ByteArrayOutputStream 分别是 ByteArrayInputStream 和 ByteArrayOutputStream 类的子类,它们可以用于对字节数组进行读写操作。

3.3.1 ByteArrayInputStream 和 ByteArrayOutputStream代码示例

3.3.1.1ByteArrayInputStream 代码示例:

import java.io.ByteArrayInputStream;

import java.io.ByteArrayInputStream;
import java.io.IOException;

public class ByteArrayInputStreamExample {
    public static void main(String[] args) throws IOException {
        // 用字符串初始化一个字节数组,作为输入数据源
        String input = "Hello, world!";
        byte[] inputBytes = input.getBytes();

        // 创建一个ByteArrayInputStream,使用输入数据源
        ByteArrayInputStream inputStream = new ByteArrayInputStream(inputBytes);

        // 读取并输出输入流中的数据
        byte[] buf = new byte[1024];
        int len;
        while ((len = inputStream.read(buf)) != -1) {
            System.out.println(new String(buf, 0, len));
        }

        // 关闭输入流
        inputStream.close();
    }
}

示例代码说明:

  • 使用“Hello, world!”字符串创建了一个字节数组作为输入数据源,并使用ByteArrayInputStream将其包装成输入流。
  • 使用一个循环从输入流中读取数据,并使用**new String()**方法将其转换成字符串并输出到控制台。
  • 最后,关闭输入流。

3.32.1.2ByteArrayOutputStream代码示例:

import java.io.ByteArrayOutputStream;
import java.io.IOException;

public class ByteArrayOutputStreamExample {
    public static void main(String[] args) {
        String input = "Hello World!";
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] output;

        try {
            outputStream.write(input.getBytes());
            output = outputStream.toByteArray();
            System.out.println(new String(output));
        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
        } finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                System.out.println("Error: " + e.getMessage());
            }
        }
    }
}

示例代码说明:

  • 在这个例子中,创建了一个ByteArrayOutputStream对象 outputStream,并向其写入一个字符串"Hello World!"。然后,我们使用**toByteArray()**方法将结果转换为一个字节数组,并打印出来。
  • 注意:在使用ByteArrayOutputStream时,要确保在不再需要它时关闭它以确保所有的字节都被刷新到输出流中。

四、总结

本文为您讲解了 Java I/O、NIO 以及其他一些流的基本概念、用法和区别。Java I/O 和 NIO 可以完成很多复杂的输入输出操作,包括文件操作、网络编程、序列化等。其他流技术可以实现压缩、读写字节数组等功能。在进行开发时,根据具体需求选择不同的流技术可以提高程序效率和开发效率。