Skip to content

IO 核心概念

你有没有想过:当你在 Java 里写 System.out.println("hello") 时,这条字符串是怎么跑到屏幕上去的?

这条语句背后,经历了从 Java 堆内存 → JVM 内部缓冲区 → 操作系统内核缓冲区 → 屏幕驱动 → 显示器,一连串的数据搬运。每一次搬运,都是一次 IO。

理解 Java 的 IO,本质上就是理解这个数据搬运的全过程。

什么是 IO

IO(Input/Output),输入与输出。在 Java 中,一切与外部世界的数据交换都叫 IO:

  • 读文件:磁盘 → 程序内存
  • 写文件:程序内存 → 磁盘
  • 网络收包:网卡 → 程序内存
  • 网络发包:程序内存 → 网卡
  • 读键盘输入:终端 → 程序内存
  • 写屏幕输出:程序内存 → 终端

核心矛盾:内存速度极快(纳秒级),而外部设备速度极慢(磁盘毫秒级,网络微秒级)。IO 的所有复杂性都来自这个速度差。

两条主线:字节与字符

Java 的 IO 围绕两条线展开:

字节流

byte 为单位处理数据,InputStream / OutputStream 是两个根类。

java
InputStream in = new FileInputStream("data.dat");
int b = in.read(); // 读 1 个字节,返回 0~255 或 -1(结束)

不管数据是什么——图片、视频、PDF、压缩包——磁盘上存的就是一串字节。所以字节流是所有 IO 的地基

字符流

char(16 位 Unicode)为单位处理数据,Reader / Writer 是两个根类。

java
Reader reader = new FileReader("text.txt");
int c = reader.read(); // 读 1 个字符,返回 Unicode 码点

字符流解决的是文本文件的读写问题——它底层用的还是字节流,只是加了一层字符编码转换。UTF-8 的「中」字是 3 个字节,但字符流一次读出来的是一个完整的字符。

输入与输出

每个流都有方向:

输入流(InputStream / Reader)  ← 数据从外部进入程序
输出流(OutputStream / Writer) ← 数据从程序输出到外部

常见混淆:FileInputStream 是输入(读文件),FileOutputStream 是输出(写文件)。

BIO 与 NIO

Java 有两套 IO 系统:

BIO(Blocking IO)

传统的流式 IO。read() 会一直阻塞,直到数据到来或流关闭。

java
// BIO 的特点是:一个线程只能处理一个连接
Socket socket = server.accept(); // 阻塞
InputStream in = socket.getInputStream();
int b = in.read(); // 这里也会阻塞

每来一个连接就创建一个线程,线程数量受限于系统资源。

NIO(New IO / Non-blocking IO)

JDK 1.4 引入。通过 Buffer(缓冲区)、Channel(通道)、Selector(选择器)三个核心组件,实现单线程管理多个连接。

java
// NIO 的特点:非阻塞 + 多路复用
selector.select(); // 阻塞,但可以同时监听多个 Channel

一个线程轮询所有连接的状态,只在连接真正可读/可写时才处理,大幅降低线程开销。

同步 vs 异步

同步(Synchronous)异步(Asynchronous)
调用方式调用后等待结果返回调用后立即返回,结果通过回调通知
线程状态等待期间阻塞不阻塞,可继续做其他事
实现BIO 和 NIO 的默认模式JDK 7+ AIO(AsynchronousChannel)

本节要点

  • IO 的本质是数据在不同存储介质之间的搬运
  • 字节流(InputStream/OutputStream)是地基,字符流(Reader/Writer)是上层建筑
  • 字符流底层是字节流,加了一层编码转换
  • BIO 是同步阻塞,NIO 是同步非阻塞多路复用,AIO 是异步非阻塞

下一步

基于 VitePress 构建