【Java第98集】java IO流详解

文章目录

  • 一、IO流的基本概念
  • 二、IO流的分类
    • 1. 按数据类型分类
      • (1)字节流(Byte Stream)
      • (2)字符流(Character Stream)
    • 2. 按方向分类
      • (1)输入流(Input Stream)
      • (2)输出流(Output Stream)
    • 3. 按功能分类
      • (1)节点流(Node Stream)
      • (2)处理流(Processing Stream)
  • 三、核心类与接口
    • 1. 字节流核心类
    • 2. 字符流核心类
    • 3. 特殊流
  • 四、IO流的使用场景
    • 1. 文件操作(File I/O)
      • (1)使用字节流复制文件(适用于二进制文件)
      • (2)使用字符流读写文本文件(支持编码处理)
    • 2. 网络通信(Socket I/O)
      • (1)简单的 TCP 服务器端
      • (2)简单的 TCP 客户端
    • 3. 对象序列化(Object I/O)
      • (1)序列化对象到文件
      • (2)反序列化对象从文件
  • 五、IO流的注意事项
    • 1. 资源管理:始终关闭流
    • 2. 异常处理:捕获并处理 `IOException`
    • 3. 缓冲区管理:使用缓冲流提升性能
    • 4. 流类型选择:区分字节流与字符流
    • 5. 字符编码:显式指定编码格式
    • 6. 文件路径处理:确保路径正确性
    • 7. 刷新缓冲区:避免数据丢失
    • 8. 并发访问:避免多线程冲突
    • 9. 性能优化:分块处理大文件
    • 10. 安全性:防止路径遍历攻击
  • 六、总结

Java IO流是Java中用于处理输入输出(Input/Output)的核心机制,是程序与外部设备(如文件、网络、内存等)之间传输数据的桥梁。以下是Java IO流的基础概念及其分类的详细说明:

一、IO流的基本概念

  • 流(Stream):流是一个抽象的数据序列,表示数据从数据源(如文件、网络)到目标(如内存、屏幕)的传输过程。数据可以是字节、字符或对象。
  • 数据源与目标:数据源是数据的来源(如文件、键盘),目标是数据的去向(如文件、网络)。
  • 方向性:IO流分为输入流(从数据源读取数据)和输出流(向目标写入数据)。

二、IO流的分类

【Java第98集】java IO流详解_第1张图片

Java IO流的分类主要基于以下三个维度:

1. 按数据类型分类

(1)字节流(Byte Stream)

  • 用途:处理二进制数据(如图片、音频、视频等)。
  • 核心类
    • InputStream:字节输入流的抽象基类。
    • OutputStream:字节输出流的抽象基类。
  • 示例
    • FileInputStream:从文件读取字节。
    • FileOutputStream:向文件写入字节。
    • BufferedInputStream/BufferedOutputStream:带缓冲的字节流,提高效率。

(2)字符流(Character Stream)

  • 用途:处理文本数据(如.txt.java文件),自动处理字符编码(如UTF-8)。
  • 核心类
    • Reader:字符输入流的抽象基类。
    • Writer:字符输出流的抽象基类。
  • 示例
    • FileReader:从文件读取字符。
    • FileWriter:向文件写入字符。
    • BufferedReader/BufferedWriter:带缓冲的字符流,支持按行读取。

2. 按方向分类

(1)输入流(Input Stream)

  • 从数据源读取数据到程序(如从文件读取内容到内存)。
  • 示例:InputStreamReader及其子类。

(2)输出流(Output Stream)

  • 将程序中的数据写入目标(如将内存数据写入文件)。
  • 示例:OutputStreamWriter及其子类。

3. 按功能分类

(1)节点流(Node Stream)

  • 直接与数据源/目标对接的流,是底层流。
  • 示例:
    • FileInputStream:直接读取文件。
    • FileOutputStream:直接写入文件。

(2)处理流(Processing Stream)

  • 对节点流进行封装,增加额外功能(如缓冲、格式化)。
  • 示例:
    • BufferedInputStream:对字节流添加缓冲功能。
    • BufferedReader:对字符流添加缓冲和按行读取功能。
    • DataInputStream/DataOutputStream:支持读写基本数据类型。
    • ObjectInputStream/ObjectOutputStream:支持对象的序列化与反序列化。

三、核心类与接口

Java IO流的核心类和接口主要位于java.io包中,以下是关键类的关系:

1. 字节流核心类

  • 输入流
    • InputStream(抽象类)
      • FileInputStream:读取文件。
      • BufferedInputStream:带缓冲的字节输入流。
      • ByteArrayInputStream:从字节数组读取数据。
  • 输出流
    • OutputStream(抽象类)
      • FileOutputStream:写入文件。
      • BufferedOutputStream:带缓冲的字节输出流。
      • ByteArrayOutputStream:将数据写入字节数组。

2. 字符流核心类

  • 输入流
    • Reader(抽象类)
      • FileReader:读取文件。
      • BufferedReader:带缓冲的字符输入流,支持readLine()方法。
      • CharArrayReader:从字符数组读取数据。
  • 输出流
    • Writer(抽象类)
      • FileWriter:写入文件。
      • BufferedWriter:带缓冲的字符输出流,支持newLine()方法。
      • CharArrayWriter:将数据写入字符数组。

3. 特殊流

  • 对象流
    • ObjectInputStream:反序列化对象。
    • ObjectOutputStream:序列化对象。
  • 转换流
    • InputStreamReader:将字节流转换为字符流(如指定编码格式)。
    • OutputStreamWriter:将字符流转换为字节流(如指定编码格式)。

四、IO流的使用场景

1. 文件操作(File I/O)

场景描述:读取和写入文件内容,适用于文本文件、图片、音频等资源的处理。

(1)使用字节流复制文件(适用于二进制文件)

import java.io.*;

public class FileCopyExample {
    public static void main(String[] args) {
        File source = new File("source.jpg");
        File dest = new File("destination.jpg");

        try (FileInputStream fis = new FileInputStream(source);
             FileOutputStream fos = new FileOutputStream(dest)) {

            byte[] buffer = new byte[1024];
            int length;

            while ((length = fis.read(buffer)) > 0) {
                fos.write(buffer, 0, length);
            }

            System.out.println("文件复制完成。");

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

(2)使用字符流读写文本文件(支持编码处理)

import java.io.*;

public class TextFileReadWrite {
    public static void main(String[] args) {
        File file = new File("example.txt");

        // 写入文本
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
            writer.write("Hello, Java IO World!");
            writer.newLine();
            writer.write("This is a sample text file.");
            System.out.println("文本已写入文件。");
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 读取文本
        try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
            String line;
            System.out.println("文件内容如下:");
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2. 网络通信(Socket I/O)

场景描述:通过 TCP/IP 协议在客户端和服务器之间传输数据,适用于远程通信、即时消息传输等场景。

(1)简单的 TCP 服务器端

import java.io.*;
import java.net.*;

public class TCPServer {
    public static void main(String[] args) {
        try (ServerSocket serverSocket = new ServerSocket(8080)) {
            System.out.println("服务器已启动,等待连接...");
            Socket socket = serverSocket.accept();

            try (BufferedReader in = new BufferedReader(
                     new InputStreamReader(socket.getInputStream()));
                 PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {

                String clientMessage = in.readLine();
                System.out.println("收到客户端消息:" + clientMessage);

                out.println("Hello from Server!");
            }

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

(2)简单的 TCP 客户端

import java.io.*;
import java.net.*;

public class TCPClient {
    public static void main(String[] args) {
        try (Socket socket = new Socket("127.0.0.1", 8080);
             PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
             BufferedReader in = new BufferedReader(
                     new InputStreamReader(socket.getInputStream()))) {

            out.println("Hello from Client!");
            String serverResponse = in.readLine();
            System.out.println("服务器响应:" + serverResponse);

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

3. 对象序列化(Object I/O)

场景描述:将 Java 对象转为字节流进行存储或传输,适用于数据持久化、远程方法调用(RMI)等场景。

(1)序列化对象到文件

import java.io.*;

public class ObjectSerialization {
    public static void main(String[] args) {
        Person person = new Person("Alice", 25);

        try (ObjectOutputStream oos = new ObjectOutputStream(
                 new FileOutputStream("person.ser"))) {
            oos.writeObject(person);
            System.out.println("对象已序列化到文件。");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

class Person implements Serializable {
    private String name;
    private int age;

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

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

(2)反序列化对象从文件

import java.io.*;

public class ObjectDeserialization {
    public static void main(String[] args) {
        try (ObjectInputStream ois = new ObjectInputStream(
                 new FileInputStream("person.ser"))) {
            Person person = (Person) ois.readObject();
            System.out.println("反序列化对象:" + person);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

五、IO流的注意事项

1. 资源管理:始终关闭流

  • 问题:未关闭的流会导致资源泄漏(如文件句柄、网络连接占用),可能引发内存溢出或系统资源耗尽。
  • 解决方案
    • 使用 try-with-resources 语句(Java 7+):自动关闭实现了 AutoCloseable 接口的资源。
    • 手动关闭流:在 finally 块中调用 close() 方法。
// 使用 try-with-resources 自动关闭流
try (FileInputStream fis = new FileInputStream("file.txt");
     FileOutputStream fos = new FileOutputStream("output.txt")) {
    int data;
    while ((data = fis.read()) != -1) {
        fos.write(data);
    }
} catch (IOException e) {
    e.printStackTrace();
}

2. 异常处理:捕获并处理 IOException

  • 问题:IO 操作可能抛出 IOException(如文件不存在、权限不足、磁盘空间不足等)。
  • 解决方案
    • 使用 try-catch 捕获异常,并记录日志或提示用户。
    • 对于多层调用,合理选择抛出异常或封装处理。
try {
    // IO 操作代码
} catch (FileNotFoundException e) {
    System.err.println("文件未找到: " + e.getMessage());
} catch (IOException e) {
    System.err.println("IO 异常: " + e.getMessage());
}

3. 缓冲区管理:使用缓冲流提升性能

  • 问题:直接使用 FileInputStream/FileOutputStream 等节点流效率较低,频繁的磁盘读写会增加 I/O 开销。
  • 解决方案
    • 包装节点流为缓冲流(BufferedInputStream/BufferedOutputStreamBufferedReader/BufferedWriter)。
    • 合理设置缓冲区大小(默认 8KB,可根据需求调整)。
try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"), 8192)) {
    String line;
    while ((line = reader.readLine()) != null) {
        // 处理每一行
    }
} catch (IOException e) {
    e.printStackTrace();
}

4. 流类型选择:区分字节流与字符流

  • 问题
    • 字节流(InputStream/OutputStream:适用于二进制数据(如图片、音频)。
    • 字符流(Reader/Writer:适用于文本数据(如 .txt 文件),自动处理字符编码。
  • 解决方案
    • 二进制文件:使用字节流(如 FileInputStream/FileOutputStream)。
    • 文本文件:使用字符流(如 FileReader/FileWriter)。
// 读取二进制文件(图片)
try (FileInputStream fis = new FileInputStream("image.jpg")) {
    byte[] buffer = new byte[1024];
    int bytesRead;
    while ((bytesRead = fis.read(buffer)) != -1) {
        // 处理字节数组
    }
} catch (IOException e) {
    e.printStackTrace();
}

5. 字符编码:显式指定编码格式

  • 问题:字符流默认使用平台编码(如 Windows 是 GBK,Linux 是 UTF-8),可能导致乱码。
  • 解决方案
    • 使用 InputStreamReader/OutputStreamWriter 指定编码(如 UTF-8)。
// 指定 UTF-8 编码读取文件
try (BufferedReader reader = new BufferedReader(
         new InputStreamReader(new FileInputStream("file.txt"), StandardCharsets.UTF_8))) {
    String line;
    while ((line = reader.readLine()) != null) {
        // 处理文本
    }
} catch (IOException e) {
    e.printStackTrace();
}

6. 文件路径处理:确保路径正确性

  • 问题
    • 路径错误(如相对路径错误、跨平台斜杠问题)。
    • 文件不存在或权限不足。
  • 解决方案
    • 使用 File 类检查文件是否存在、是否可读/写。
    • 跨平台使用 File.separatorPaths.get()
File file = new File("path/to/file.txt");
if (file.exists() && file.canRead()) {
    try (FileReader reader = new FileReader(file)) {
        // 读取操作
    } catch (IOException e) {
        e.printStackTrace();
    }
} else {
    System.err.println("文件不存在或无法读取");
}

7. 刷新缓冲区:避免数据丢失

  • 问题:使用 BufferedWriter/PrintWriter 等缓冲流时,数据可能滞留在缓冲区未写入目标。
  • 解决方案
    • 在关键操作后调用 flush() 方法强制刷新缓冲区。
    • 或在流关闭时自动刷新(close() 会调用 flush())。
try (BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))) {
    writer.write("Hello");
    writer.flush(); // 手动刷新
    writer.newLine(); // 写入换行符
} catch (IOException e) {
    e.printStackTrace();
}

8. 并发访问:避免多线程冲突

  • 问题:多个线程同时读写同一文件可能导致数据不一致。
  • 解决方案
    • 使用同步机制(如 synchronized 块)。
    • 或使用线程安全的类(如 RandomAccessFile)。
synchronized (fileLock) {
    try (FileWriter writer = new FileWriter("shared.txt", true)) {
        writer.write("Thread-safe write\n");
    } catch (IOException e) {
        e.printStackTrace();
    }
}

9. 性能优化:分块处理大文件

  • 问题:一次性加载大文件到内存可能导致内存溢出。
  • 解决方案
    • 分块读取/写入(如按行或按固定大小读取)。
    • 使用 NIO 的 FileChannel 进行内存映射(MappedByteBuffer)。
// 分块读取大文件
try (FileInputStream fis = new FileInputStream("largefile.txt")) {
    byte[] buffer = new byte[8192];
    int bytesRead;
    while ((bytesRead = fis.read(buffer)) != -1) {
        // 处理分块数据
    }
} catch (IOException e) {
    e.printStackTrace();
}

10. 安全性:防止路径遍历攻击

  • 问题:用户输入路径可能包含 ../ 等特殊字符,导致越权访问。
  • 解决方案
    • 验证和规范化路径(如 File.getCanonicalPath())。
    • 限制文件访问范围(如只允许访问特定目录)。
String userInputPath = "user/../etc/passwd";
File file = new File("/safe/directory/" + userInputPath);
File normalizedFile = file.getCanonicalFile();
if (normalizedFile.getParentFile().getPath().startsWith("/safe/directory")) {
    // 安全访问
} else {
    throw new SecurityException("非法路径");
}

六、总结

Java IO流是程序与外部世界交互的核心机制,通过灵活的分类和组合,能够高效处理各种数据传输需求。掌握字节流、字符流、缓冲流及对象流的使用,是开发文件处理、网络通信等应用的基础。

你可能感兴趣的:(Java基础,java,开发语言)