核心类库_IO流

162 阅读9分钟

Java IO(Input/Output)是 Java 中处理数据输入输出的核心 API,用于实现程序与外部设备(如文件、网络、内存等)的数据交互。

IO流核心概念

流(Stream)

  • 定义:流是 Java 中数据传输的抽象表示,如同 “水流” 一样,数据以有序的、连续的方式在程序与外部设备(文件、网络、内存等)之间传输。
  • 特性
    • 流是单向的:输入流只能读数据,输出流只能写数据。
    • 流是顺序的:数据按顺序传输,不能随机访问(如不能直接跳到流的中间位置读写)。
    • 流是可关闭的:流会占用系统资源(如文件句柄),使用后必须关闭以释放资源。

输入与输出(Input/Output)

  • 输入(Input:数据从外部设备(如文件、键盘、网络)进入程序的过程(读操作),由输入流(InputStream/Reader 体系)实现。
  • 输出(Output:数据从程序发送到外部设备(如文件、屏幕、网络)的过程(写操作),由输出流(OutputStream/Writer 体系)实现。
  • 方向基准:以程序为参照物,数据 “进入程序” 为输入,“离开程序” 为输出。

数据源(Data Source)与数据目的地(Data Sink)

  • 数据源:提供数据的源头,是输入流的读取对象,如:
    • 物理设备:文件、硬盘、U 盘。
    • 网络资源:Socket 连接、HTTP 响应。
    • 内存资源:字节数组、字符数组。
  • 数据目的地:接收数据的目标,是输出流的写入对象,与数据源类型对应(文件、网络、内存等)。

缓冲(Buffer)机制

  • 定义:缓冲是处理流(如 BufferedInputStreamBufferedReader)内部维护的一块内存区域,用于临时存储数据。
  • 作用:减少直接与外部设备的 IO 交互次数(如磁盘读写、网络传输),因为内存操作比外部设备操作快得多,从而大幅提升效率。
  • 原理
    • 读操作:先将数据从外部设备读入缓冲区,程序从缓冲区取数据(缓冲区空了再读外部设备)。
    • 写操作:程序先将数据写入缓冲区,缓冲区满了再一次性写入外部设备(可通过 flush() 强制刷新)。

编码与解码(字符流核心)

  • 编码(Encode):字符 → 字节(如将 '中' 转换为 UTF-8 编码的字节 [-28, -72, -83])。
  • 解码(Decode):字节 → 字符(如将 UTF-8 字节 [-28, -72, -83] 转换为 '中')。
  • 字符流的本质:字符流 = 字节流 + 编码表,通过 InputStreamReader/OutputStreamWriter 可指定编码(如 UTF-8),避免文本处理时的乱码问题。

流的分类体系

整体概览:

画板

Java 流(Stream)的分类体系是理解 IO 操作的核心框架,主要通过三个维度进行划分,形成了层次清晰的分类体系

按数据流向分类

以程序为参照物,根据数据传输的方向划分:

类型定义核心基类典型用途
输入流数据从外部设备进入程序(读操作)InputStream(字节)、Reader(字符)读取文件、接收网络数据等
输出流数据从程序发送到外部设备(写操作)OutputStream(字节)、Writer(字符)写入文件、发送网络数据等

特点:输入流只能读不能写,输出流只能写不能读,单向传输是流的核心特性。

按处理数据的单位分类

根据流操作的数据最小单位划分:
  1. 字节流
  • 单位:以字节(8 位二进制)为基本处理单位。
  • 适用场景:所有类型数据(文本、图片、音频、视频等二进制数据)。
  • 核心基类
    • 输入:InputStream(抽象类,所有字节输入流的父类)。
    • 输出:OutputStream(抽象类,所有字节输出流的父类)。
  • 典型实现类
    • 节点流:FileInputStreamFileOutputStream(直接操作文件字节)。
    • 处理流:BufferedInputStreamBufferedOutputStream(缓冲增强)、DataInputStreamDataOutputStream(读写基本数据类型)。
  1. 字符流
  • 单位:以字符(16 位 Unicode 编码)为基本处理单位。
  • 适用场景:仅文本数据(如 .txt、.java 文件),自动处理编码转换。
  • 核心基类
    • 输入:Reader(抽象类,所有字符输入流的父类)。
    • 输出:Writer(抽象类,所有字符输出流的父类)。
  • 典型实现类
    • 节点流:FileReaderFileWriter(直接操作文本,但依赖系统编码)。
    • 处理流:InputStreamReaderOutputStreamWriter(字节流转字符流,指定编码)、BufferedReaderBufferedWriter(缓冲 + 按行读写)。

核心区别:字节流处理原始二进制数据,字符流处理经过编码的文本数据(解决乱码问题)。

按功能角色分类

根据流与数据源的关系及功能划分:
  1. 节点流(低级流)
  • 定义:直接连接数据源(如文件、内存、网络)的流,是 IO 操作的 “底层通道”。
  • 特点:必须与具体数据源绑定,无法独立存在,是所有流操作的基础。
  • 典型实现
    • 字节节点流:FileInputStreamFileOutputStream(连接文件)、ByteArrayInputStream(连接内存字节数组)。
    • 字符节点流:FileReaderFileWriter(连接文本文件)。
  1. 处理流(高级流 / 包装流)
  • 定义:不直接连接数据源,而是包装节点流或其他处理流,用于增强功能或简化操作。
  • 特点:依赖被包装的流(“装饰者模式”),可多层嵌套,提升效率或扩展功能。
  • 典型实现及功能
    • 缓冲流(BufferedXXX):增加缓冲区,减少 IO 次数,提高效率。
    • 转换流(InputStreamReader/OutputStreamWriter):字节流与字符流互转,指定编码。
    • 对象流(ObjectInputStream/ObjectOutputStream):实现对象的序列化与反序列化。
    • 打印流(PrintWriter):提供格式化输出、自动换行等便捷功能。

分类体系总结表

分类维度细分类型核心基类 / 接口核心特点典型场景
数据流向输入流InputStreamReader读数据到程序,单向读取文件、接收网络数据
输出流OutputStreamWriter从程序写数据,单向写入文件、发送网络数据
数据单位字节流InputStreamOutputStream处理字节,适用于所有数据类型图片、视频、音频等
字符流ReaderWriter处理字符,适用于文本,解决编码问题文本文件读写
功能角色节点流FileInputStream直接连接数据源,底层基础流与文件、内存直接交互
处理流BufferedReader包装其他流,增强功能(缓冲、转换等)提高效率、简化操作

常用类

字节流(处理二进制数据)

字节流以byte为单位处理数据,适用于所有类型文件(如图片、音频、视频、文本等)。核心基类是抽象类InputStream(输入)和OutputStream(输出)。

输入字节流(InputStream)

FileInputStream
从文件读取字节数据

常用方法read()(读单个字节)、read(byte[] b)(读字节到数组)、close()(关闭流)。

// 读取文件内容(字节流)
try (FileInputStream fis = new FileInputStream("test.txt")) {
    byte[] buffer = new byte[1024]; // 缓冲区
    int len;
    // 循环读取,len为实际读取的字节数(-1表示结束)
    while ((len = fis.read(buffer)) != -1) {
        System.out.println(new String(buffer, 0, len)); // 转成字符串输出
    }
} catch (IOException e) {
    e.printStackTrace();
}
BufferedInputStream

带缓冲区的字节输入流,通过减少 IO 次数提高效率(包装其他 InputStream)。

常用方法与InputStream一致,底层维护缓冲区。

// 缓冲流读取(效率更高)
try (BufferedInputStream bis = new BufferedInputStream(
        new FileInputStream("test.txt"))) {
    byte[] buffer = new byte[1024];
    int len;
    while ((len = bis.read(buffer)) != -1) {
        System.out.println(new String(buffer, 0, len));
    }
} catch (IOException e) {
    e.printStackTrace();
}
ByteArrayInputStream

从字节数组读取数据(内存中的输入流)。

适用于需要将字节数组当作 “数据源” 的场景。

输出字节流(OutputStream)

FileOutputStream
向文件写入字节数据。

常用方法write(int b)(写单个字节)、write(byte[] b)(写字节数组)、flush()(刷新缓冲区)、close()

// 写入文件(字节流)
String content = "Hello, IO Stream!";
try (FileOutputStream fos = new FileOutputStream("output.txt")) {
    fos.write(content.getBytes()); // 字符串转字节数组写入
    fos.flush(); // 强制刷新(确保数据写入磁盘)
} catch (IOException e) {
    e.printStackTrace();
}
BufferedOutputStream

带缓冲区的字节输出流,减少 IO 次数(包装其他 OutputStream)。

// 缓冲流写入
try (BufferedOutputStream bos = new BufferedOutputStream(
        new FileOutputStream("output.txt"))) {
    bos.write("Buffered Output".getBytes());
    bos.flush(); // 缓冲流需手动刷新(或关闭时自动刷新)
} catch (IOException e) {
    e.printStackTrace();
}
ByteArrayOutputStream

向字节数组写入数据(内存中的输出流),可通过toByteArray()获取结果。

字符流(处理文本数据)

字符流以char为单位处理数据(适合文本文件,如.txt.java),核心基类是抽象类Reader(输入)和Writer(输出)。

输入字符流(Reader)

FileReader
从文件读取字符数据(默认使用系统编码)。

常用方法read()(读单个字符)、read(char[] cbuf)(读字符到数组)、close()

// 字符流读取文本
try (FileReader fr = new FileReader("text.txt")) {
    char[] buffer = new char[1024];
    int len;
    while ((len = fr.read(buffer)) != -1) {
        System.out.println(new String(buffer, 0, len));
    }
} catch (IOException e) {
    e.printStackTrace();
}
BufferedReader
带缓冲区的字符输入流,提供`readLine()`方法(读取整行文本),效率更高。
// 缓冲字符流读取(支持读行)
try (BufferedReader br = new BufferedReader(
        new FileReader("text.txt"))) {
    String line;
    // 逐行读取(null表示结束)
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
    e.printStackTrace();
}
StringReader
从字符串读取字符数据(内存中的字符输入流)。

输出字符流(Writer)

FileWriter
向文件写入字符数据(默认系统编码)。

常用方法write(int c)(写单个字符)、write(char[] cbuf)write(String str)(写字符串)、flush()close()

// 字符流写入文本
try (FileWriter fw = new FileWriter("output.txt")) {
    fw.write("Hello, 字符流!"); // 直接写字符串
    fw.flush();
} catch (IOException e) {
    e.printStackTrace();
}
BufferedWriter

带缓冲区的字符输出流,提供newLine()方法(跨平台换行)。

// 缓冲字符流写入(支持换行)
try (BufferedWriter bw = new BufferedWriter(
        new FileWriter("output.txt"))) {
    bw.write("第一行");
    bw.newLine(); // 换行(自动适配系统)
    bw.write("第二行");
    bw.flush();
} catch (IOException e) {
    e.printStackTrace();
}
StringWriter

向字符串缓冲区写入字符数据,可通过toString()获取结果。

转换流(字节流 ↔ 字符流)

字节流与字符流的桥梁,用于指定编码格式处理文本(解决中文乱码问题)。

  • InputStreamReader:将字节输入流转换为字符输入流(指定编码)。
  • OutputStreamWriter:将字节输出流转换为字符输出流(指定编码)。
// 指定编码读取(解决乱码)
try (InputStreamReader isr = new InputStreamReader(
        new FileInputStream("text.txt"), "UTF-8"); // 指定UTF-8编码
     BufferedReader br = new BufferedReader(isr)) {
    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
    e.printStackTrace();
}

// 指定编码写入
try (OutputStreamWriter osw = new OutputStreamWriter(
        new FileOutputStream("output.txt"), "UTF-8"); // 指定UTF-8编码
     BufferedWriter bw = new BufferedWriter(osw)) {
    bw.write("中文内容(UTF-8编码)");
} catch (IOException e) {
    e.printStackTrace();
}

其他常用流

DataInputStream / DataOutputStream

读写基本数据类型(如intdouble),保持数据类型不变。

// 写入基本类型
try (DataOutputStream dos = new DataOutputStream(
        new FileOutputStream("data.bin"))) {
    dos.writeInt(100);
    dos.writeDouble(3.14);
    dos.writeUTF("字符串"); // 写入UTF-8字符串
} catch (IOException e) {
    e.printStackTrace();
}

// 读取基本类型
try (DataInputStream dis = new DataInputStream(
        new FileInputStream("data.bin"))) {
    int num = dis.readInt();
    double d = dis.readDouble();
    String str = dis.readUTF();
    System.out.println(num + ", " + d + ", " + str);
} catch (IOException e) {
    e.printStackTrace();
}

ObjectInputStream / ObjectOutputStream

用于对象的序列化(写入)和反序列化(读取),要求类实现Serializable接口。

// 序列化对象
class User implements Serializable {
    String name;
    int age;
    // 构造方法、getter/setter...
}

try (ObjectOutputStream oos = new ObjectOutputStream(
        new FileOutputStream("user.obj"))) {
    User user = new User("Alice", 20);
    oos.writeObject(user); // 写入对象
} catch (IOException e) {
    e.printStackTrace();
}

// 反序列化对象
try (ObjectInputStream ois = new ObjectInputStream(
        new FileInputStream("user.obj"))) {
    User user = (User) ois.readObject(); // 读取对象
    System.out.println(user.name + ", " + user.age);
} catch (IOException | ClassNotFoundException e) {
    e.printStackTrace();
}