Java.IO
目录
1.3 NIO(Non-blocking IO,非阻塞 IO)
1.4 AIO(Asynchronous IO,异步 IO)
1.理解IO,BIO,NIO,AIO。
要理解 IO、BIO、NIO、AIO,需要从IO 模型的角度切入 —— 它们本质是不同场景下数据输入输出的处理方式,核心区别体现在同步 / 异步和阻塞 / 非阻塞的特性上。、
简单比喻:
- BIO:点餐后站在窗口一直等餐(阻塞,啥也干不了)。
- NIO:点餐后每隔一会儿去窗口问 “好了吗”(非阻塞,期间可干别的)。
- AIO:点餐后留电话,餐好了商家打电话通知(异步,完全不用管)。
1.1基础:IO(Input/Output,输入输出)
IO 是所有数据交换过程的统称,指计算机与外部设备(如磁盘、网络)或其他系统之间的数据读取(Input)和写入(Output)操作。例如:读取本地文件、接收网络数据、向数据库写入数据等,都属于 IO 操作。
1.2 BIO(Blocking IO,阻塞 IO)
BIO 是同步阻塞 IO模型,是最传统的 IO 处理方式。
核心特点:
- 阻塞性:当发起 IO 操作(如读 / 写)时,线程会被「挂起」,直到操作完成(如数据读完、写入成功)才能继续执行其他任务。
- 同步性:IO 操作的发起者(应用线程)必须等待操作结果,不能 “撒手不管”。
举例:
传统的 Java 文件读取(FileInputStream)或 Socket 通信:
// BIO读取文件(阻塞示例)
InputStream in = new FileInputStream("file.txt");
byte[] buffer = new byte[1024];
int len = in.read(buffer); // 线程阻塞,直到读取到数据或结束
如果读取的文件很大或网络数据未到达,线程会一直卡在read()方法,期间无法做任何事。
问题:
- 并发场景下,每处理一个 IO 任务需占用一个线程(如服务器每接一个客户端连接就新建线程),线程资源有限(JVM 线程数通常上限为几千),高并发时会导致线程耗尽、性能崩溃。
- 适用场景:连接数少、操作简单的场景(如简单的本地文件处理)。
1.3 NIO(Non-blocking IO,非阻塞 IO)
NIO 是同步非阻塞 IO模型,JDK 1.4 引入,用于解决 BIO 的高并发瓶颈。
核心特点:
- 非阻塞性:IO 操作发起后,若数据未就绪,线程不会阻塞,而是立即返回(通常返回 0 或空),可以继续执行其他任务。
- 同步性:应用线程仍需主动「轮询」检查 IO 操作是否就绪(而非被动等待通知)。
- 多路复用:通过「选择器(Selector)」管理多个 IO 通道(Channel),一个线程可同时处理多个 IO 任务,大幅减少线程数量。
核心组件:
- Channel(通道):类似流,但双向(可读可写),支持非阻塞操作(如
SocketChannel、FileChannel)。 - Buffer(缓冲区):数据的容器(如
ByteBuffer),IO 操作需通过缓冲区进行(先读数据到缓冲区,再从缓冲区取数据)。 - Selector(选择器):监听多个 Channel 的事件(如 “读就绪”“连接就绪”),一个线程通过 Selector 即可管理所有 Channel,实现 “多路复用”。
1.4 AIO(Asynchronous IO,异步 IO)
AIO 是异步非阻塞 IO模型,也叫 NIO.2,JDK 1.7 引入,进一步提升 IO 效率。
核心特点:
- 非阻塞性:IO 操作发起后,线程无需等待,可立即执行其他任务。
- 异步性:IO 操作由操作系统完成后,通过「回调函数」主动通知应用线程(无需应用轮询)。
2.流写入与写出(基础版)
在编程中,流的写入与输出是数据处理的核心操作。流(Stream)是一种抽象的数据序列,可以是文件、网络连接或内存缓冲区。
(这里为了方便展示输出,采用字节流读取文本,实际上用字符流更好,这里可以把文本内容换成一首歌等等)
2.1流写入( 输入: 吸星大法 从源读取)
核心逻辑:
程序通过输入流,从 "数据源"(文件、网络、Socket 等)中主动读取数据,数据的流动方向是「源 → 程序」。
关键特点:
- 数据源必须存在(否则会抛出异常,如文件不存在时
FileNotFoundException)。 - 读取过程是 "被动" 的:程序需要主动调用
read()等方法才能获取数据。 - 读完数据后必须关闭流,释放数据源的占用(如文件句柄、网络连接)。
常用输入流类及场景:
| 类型 | 典型类 | 数据源场景 | 核心方法(吸星动作) |
|---|---|---|---|
| 字节输入流 | FileInputStream | 本地二进制文件(图片、视频) | read(byte[] buf):批量读取字节 |
Socket.getInputStream() | 网络 Socket(对方发送的数据) | read():逐个读取字节 | |
| 字符输入流 | BufferedReader | 本地文本文件、网络文本响应 | readLine():读取一行文本(高效) |
InputStreamReader | 字节流转字符流(指定编码) | read(char[] cbuf):批量读取字符 |
示例:用 "吸星大法" 读取文本文件
import java.io.*;
public class Test {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();//创建一个StringBuilder对象
InputStream in1=null;
try {
in1 = new FileInputStream("./user.ser");//创建输入流
int b;
// 字节数组,缓冲区,提高流的读写性能,减少流的读写次数
// 就像你家的米舀子
byte[] buffer = new byte[1024];
//循环读取,吸数据
while ((b = in1.read(buffer)) != -1) {
sb.append(new String(buffer, 0, b)); // 只添加实际读取的字节
System.out.println(sb.toString());
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}catch (IOException e) {
throw new RuntimeException(e);
}
finally {
if (null != in1) {
try {
in1.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}//关闭输入流
}
}
}
输出结果:

2.2流写出( 输出,北冥神功 写到目标 )
核心逻辑:
程序通过输出流,将数据主动写入到 "目标"(文件、网络、Socket 等),数据的流动方向是「程序 → 目标」。
关键特点:
- 目标可以不存在(如文件不存在时,输出流会自动创建,目录除外)。
- 写入过程是 "主动" 的:程序调用
write()等方法将数据推送到目标。 - 写完后需刷新(flush) 或关闭流:缓冲流会先将数据存在内存缓冲区,刷新后才真正写入目标(关闭时会自动刷新)。
常用输出流类及场景:
| 类型 | 典型类 | 目标场景 | 核心方法(北冥动作) |
|---|---|---|---|
| 字节输出流 | FileOutputStream | 本地二进制文件(图片、视频) | write(byte[] buf):批量写入字节 |
Socket.getOutputStream() | 网络 Socket(向对方发数据) | write(int b):逐个写入字节 | |
| 字符输出流 | BufferedWriter | 本地文本文件、网络文本发送 | write(String str):写入字符串 |
PrintWriter | 格式化输出(如日志、控制台) | println(String str):写入一行并换 |
示例:用 "北冥神功" 写入文本文件
import java.io.*;
public class Test {
public static void main(String[] args) {
System.out.println("开始北冥神功");
try (
// 字符输出流(带缓冲,提高北冥效率)
BufferedWriter writer = new BufferedWriter(new FileWriter("user.ser",true))// 创建字符输出流
) {
String data = "这是要导出的数据";
// 主动"吐"数据到目标
writer.write(data);
writer.newLine(); // 换行(跨平台兼容)
writer.write("第二行数据");
// 手动刷新(可选,关闭时会自动刷新)
writer.flush();// 刷新缓冲区
System.out.println("北冥神功完成,数据已导出");
} catch (IOException e) {
e.printStackTrace();
}
}
}
注意点:
BufferedWriter 会覆盖原文件内容的原因如下:
FileWriter 默认行为:
当使用 new FileWriter("user.ser") 构造函数时,默认以覆盖模式打开文件
这相当于 new FileWriter("user.ser", false),第二个参数默认为 false,改成true即可
Java IO 机制:
FileWriter 基于 FileOutputStream,默认会截断(truncate)已有文件
文件指针定位在开头位置,新写入的内容会从头开始覆盖
输出效果:

2.3输入与输出的 "组合技":数据搬运
实际开发中,输入流和输出流往往配合使用,完成 "源 → 程序 → 目标" 的完整数据搬运,比如文件复制、网络数据转发等。
示例:组合 "吸星 + 北冥" 实现文件复制
import java.io.*;
public class Main {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();//创建一个StringBuilder对象
InputStream in1=null;
OutputStream out1=null;
try {
in1 = new FileInputStream("./user.ser");//创建输入流
File f = new File("D://user//a.txt");//创建文件对象
if (!f.getParentFile().exists()) {
f.getParentFile().mkdirs();
}//创建父目录
out1 = new FileOutputStream(f);//创建输出流
int b;
// 字节数组,缓冲区,提高流的读写性能,减少流的读写次数
// 就像你家的米舀子
byte[] buffer = new byte[1024];
//循环读取,吸数据
while ((b = in1.read(buffer)) != -1) {
out1.write(buffer);
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}catch (IOException e) {
throw new RuntimeException(e);
}
finally {
if (null != in1) {
try {
in1.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}//关闭输入流
}
}
}
运行效果:

3.java.io常用的类有哪些
(微信公众号看到的比较齐全的图)

1.实际操作常用的用法
他们的类看着非常多而且繁琐,建议组合使用。
例如:
FileInputStream
用途: 用于从文件系统中读取文件数据
特点: 直接与文件关联,每次读取都会触发系统调用
性能: 频繁的小量读取操作效率较低
BufferedInputStream
用途: 为其他输入流添加缓冲功能
特点:
内部维护缓冲区,减少系统调用次数
显著提升频繁读取操作的性能
支持 mark() 和 reset() 方法
用法: 通常包装 FileInputStream 或其他基础输入流
// 推荐的组合使用方式
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("file.txt"));
这样的方式结合了 FileInputStream 的文件访问能力和 BufferedInputStream 的缓冲优化。
更多推荐


所有评论(0)