前言

IO(Input/Output)流是 Java 开发的核心基础,也是面试高频考点,无论是文件读写、网络通信,还是数据序列化,都离不开 IO 流的支撑。本文将从 IO 流的分类、核心 API、字节流 / 字符流、对象流、NIO 等维度,全面详细讲解 Java IO 流的知识体系,搭配实战代码示例,帮你彻底掌握 IO 流的核心逻辑与应用场景。

一、IO 流核心概念

1. 什么是 IO 流

IO 流是 Java 中用于数据传输的抽象概念,本质是数据的流动通道,分为输入流(Input)和输出流(Output):

  • 输入流:从外部设备(文件、网络、键盘等)读取数据到程序内存中;
  • 输出流:将程序内存中的数据写入到外部设备(文件、网络、显示器等)。

2. IO 流的分类(核心)

Java IO 流的分类维度有 3 个,需重点掌握:

(1)按数据传输单位分
  • 字节流:以字节(Byte) 为单位传输,可处理所有类型数据(文本、图片、音频、视频等),基类为InputStream(输入)、OutputStream(输出);
  • 字符流:以字符(Char) 为单位传输,仅能处理文本数据,自动适配编码(UTF-8、GBK 等),基类为Reader(输入)、Writer(输出)。
(2)按数据流向分
  • 输入流InputStreamReader
  • 输出流OutputStreamWriter
(3)按功能角色分
  • 节点流:直接连接数据源(文件、网络等),是数据传输的 “直接通道”,如FileInputStreamFileReader
  • 处理流(包装流):基于节点流封装,增强功能(缓冲、序列化、编码转换等),如BufferedInputStreamObjectOutputStream

3. IO 流的继承体系(必记)

plaintext

# 字节流基类
InputStream(抽象)
├── FileInputStream(文件字节输入流)
├── BufferedInputStream(缓冲字节输入流)
├── ObjectInputStream(对象输入流)
└── ...

OutputStream(抽象)
├── FileOutputStream(文件字节输出流)
├── BufferedOutputStream(缓冲字节输出流)
├── ObjectOutputStream(对象输出流)
└── ...

# 字符流基类
Reader(抽象)
├── FileReader(文件字符输入流)
├── BufferedReader(缓冲字符输入流)
└── ...

Writer(抽象)
├── FileWriter(文件字符输出流)
├── BufferedWriter(缓冲字符输出流)
└── ...

二、字节流(InputStream/OutputStream)

字节流是 IO 流的基础,支持所有数据类型,核心 API 和实战如下:

1. FileInputStream(文件字节输入流)

作用:读取文件中的字节数据,适用于非文本文件(图片、音频)和文本文件。

核心方法
  • int read():读取一个字节,返回字节值(0-255),读取到文件末尾返回 - 1;
  • int read(byte[] b):读取字节数组,返回读取的字节数;
  • void close():关闭流,释放资源(必须手动关闭,避免资源泄漏)。
实战:读取图片文件

java

运行

import java.io.FileInputStream;
import java.io.IOException;

public class ByteInputDemo {
    public static void main(String[] args) {
        // 声明流对象(try-with-resources自动关闭流,推荐)
        try (FileInputStream fis = new FileInputStream("test.jpg")) {
            byte[] buffer = new byte[1024]; // 缓冲区,提升读取效率
            int len; // 记录每次读取的字节数
            while ((len = fis.read(buffer)) != -1) {
                // 处理读取的字节数据(如写入新文件、解析数据等)
                System.out.println("读取字节数:" + len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2. FileOutputStream(文件字节输出流)

作用:将字节数据写入文件,支持覆盖写入和追加写入。

核心方法
  • void write(int b):写入一个字节;
  • void write(byte[] b):写入字节数组;
  • void write(byte[] b, int off, int len):写入字节数组的指定部分;
  • 构造器:FileOutputStream(String name, boolean append)append=true表示追加写入,默认 false(覆盖)。
实战:写入文本到文件

java

运行

import java.io.FileOutputStream;
import java.io.IOException;

public class ByteOutputDemo {
    public static void main(String[] args) {
        String content = "Java IO流学习笔记";
        try (FileOutputStream fos = new FileOutputStream("io.txt", true)) { // 追加模式
            fos.write(content.getBytes()); // 字符串转字节数组写入
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3. 缓冲字节流(BufferedInputStream/BufferedOutputStream)

作用:基于节点流封装,内置缓冲区(默认 8KB),减少磁盘 IO 次数,大幅提升读写效率,是开发中常用的处理流。

实战:缓冲流复制文件

java

运行

import java.io.*;

public class BufferedCopyDemo {
    public static void main(String[] args) {
        try (
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream("source.jpg"));
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("target.jpg"))
        ) {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, len);
            }
            bos.flush(); // 刷新缓冲区,确保数据写入文件
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

三、字符流(Reader/Writer)

字符流专为文本数据设计,自动处理编码问题,避免中文乱码,核心 API 和实战如下:

1. FileReader(文件字符输入流)

作用:读取文本文件中的字符数据,默认使用系统编码(Windows 为 GBK,Linux 为 UTF-8)。

核心方法
  • int read():读取一个字符,返回字符的 ASCII 码,末尾返回 - 1;
  • int read(char[] cbuf):读取字符数组,返回读取的字符数。

2. FileWriter(文件字符输出流)

作用:将字符数据写入文本文件,支持追加写入。

实战:字符流读写文本

java

运行

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class CharDemo {
    public static void main(String[] args) {
        // 读取文本
        try (FileReader fr = new FileReader("io.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();
        }

        // 写入文本(追加)
        try (FileWriter fw = new FileWriter("io.txt", true)) {
            fw.write("\n字符流写入测试");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3. 缓冲字符流(BufferedReader/BufferedWriter)

作用:内置缓冲区,支持按行读写(readLine()newLine()),是文本处理的首选。

实战:按行读取文本

java

运行

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class BufferedReaderDemo {
    public static void main(String[] args) {
        try (BufferedReader br = new BufferedReader(new FileReader("io.txt"))) {
            String line;
            // 按行读取,直到末尾返回null
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

4. 转换流(InputStreamReader/OutputStreamWriter)

作用:字节流与字符流的桥梁,可指定编码格式,解决中文乱码问题(核心!)。

实战:指定 UTF-8 编码读写文件

java

运行

import java.io.*;

public class ConvertStreamDemo {
    public static void main(String[] args) {
        // 字节流转字符流,指定UTF-8编码
        try (
            InputStreamReader isr = new InputStreamReader(new FileInputStream("io.txt"), "UTF-8");
            BufferedReader br = new BufferedReader(isr)
        ) {
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

四、对象流(ObjectInputStream/ObjectOutputStream)

对象流用于对象的序列化与反序列化,可将 Java 对象写入文件或网络,也可读取还原对象,是分布式开发、缓存存储的核心技术。

1. 核心概念

  • 序列化:将内存中的 Java 对象转换为字节序列,写入外部设备(ObjectOutputStream);
  • 反序列化:将外部设备中的字节序列还原为 Java 对象(ObjectInputStream);
  • 序列化要求:对象必须实现Serializable接口(标记接口,无抽象方法),否则抛出NotSerializableException

2. 实战:对象序列化与反序列化

(1)定义可序列化的实体类

java

运行

import java.io.Serializable;

// 实现Serializable接口,支持序列化
public class Student implements Serializable {
    // 序列化版本ID,确保反序列化时类的一致性
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;

    // 构造器、get/set、toString 省略
}
(2)序列化(写入对象)

java

运行

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;

public class ObjectOutputDemo {
    public static void main(String[] args) {
        Student student = new Student("张三", 18);
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("student.dat"))) {
            oos.writeObject(student); // 序列化对象
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
(3)反序列化(读取对象)

java

运行

import java.io.FileInputStream;
import java.io.ObjectInputStream;

public class ObjectInputDemo {
    public static void main(String[] args) {
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("student.dat"))) {
            Student student = (Student) ois.readObject(); // 反序列化
            System.out.println(student);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3. 对象流的 “多对象” 陷阱(高频面试)

坑点

连续writeObject()写入同一个对象(修改属性后),反序列化读取的始终是第一次写入的旧对象

原因

ObjectOutputStream 内部维护对象引用缓存表,避免重复序列化,第二次写入仅存储引用编号,不重新序列化。

解决方案
  1. 调用reset()清空缓存(推荐):oos.reset(); 后再写入;
  2. 每次新建ObjectOutputStream:避免缓存复用;
  3. 重写removeClassDesc方法:强制移除类描述符缓存(高级用法)。

五、NIO(非阻塞 IO)

Java NIO(New IO/Non-blocking IO)是 Java 1.4 引入的新 IO 模型,解决了传统 BIO(阻塞 IO)的并发瓶颈,是现代高并发网络编程的基础。

1. BIO vs NIO 核心对比

表格

维度 BIO(阻塞 IO) NIO(非阻塞 IO)
IO 模型 阻塞式 非阻塞式
数据传输 流式(Stream),单向 块式(Channel+Buffer),双向
线程模型 一连接一线程,并发低 一线程多连接,并发高
核心组件 InputStream/OutputStream Buffer、Channel、Selector
适用场景 低并发、小流量 高并发、长连接(IM、网关)

2. NIO 三大核心组件

(1)Buffer(缓冲区)
  • 本质是内存数组,用于存储 NIO 传输的数据,所有数据都通过 Buffer 交互;
  • 核心属性:capacity(容量)、position(当前位置)、limit(限制);
  • 常用类型:ByteBuffer(最常用)、CharBufferIntBuffer等。
(2)Channel(通道)
  • 数据传输的双向通道,支持读、写、读写同时操作;
  • 常用类型:FileChannel(文件通道)、SocketChannel(网络客户端通道)、ServerSocketChannel(网络服务端通道)。
(3)Selector(选择器)
  • 核心组件,用于监听多个 Channel 的事件(连接、读、写);
  • 一个 Selector 可管理多个 Channel,实现单线程处理多连接,大幅提升并发能力。

3. NIO 实战:文件复制

java

运行

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class NioCopyDemo {
    public static void main(String[] args) {
        try (
            FileChannel inChannel = new FileInputStream("source.jpg").getChannel();
            FileChannel outChannel = new FileOutputStream("target.jpg").getChannel()
        ) {
            ByteBuffer buffer = ByteBuffer.allocate(1024); // 分配缓冲区
            while (inChannel.read(buffer) != -1) {
                buffer.flip(); // 切换为读模式
                outChannel.write(buffer);
                buffer.clear(); // 清空缓冲区
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

六、IO 流使用注意事项

  1. 流的关闭:使用try-with-resources语法自动关闭流,避免资源泄漏;
  2. 编码问题:处理中文文本时,使用转换流指定编码(UTF-8),避免乱码;
  3. 缓冲流优先:读写文件时,优先使用缓冲流,提升 IO 效率;
  4. 序列化安全:序列化类需指定serialVersionUID,避免类修改后反序列化失败;
  5. NIO 适用场景:高并发网络编程用 NIO,普通文件读写用传统 IO 即可。

七、总结

本文全面讲解了 Java IO 流的核心知识,从基础的字节流 / 字符流,到进阶的对象流、NIO,覆盖了 IO 流的分类、API、实战和面试考点。

  • 基础应用:文件读写、文本处理,优先使用缓冲字节流 / 字符流;
  • 进阶应用:对象序列化、网络通信,使用对象流和 NIO;
  • 核心原则:合理选择流类型,及时关闭流,优化 IO 效率。

掌握 IO 流是 Java 开发的必备技能,后续可深入学习 NIO.2(AIO)、Netty 等高性能 IO 框架,进一步提升高并发编程能力。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐