- 常见的流
1.1 字节流
常用类如下

File读取文件
public static void main(String[] args) {
try (
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("C:/Users/gaoxinyu/Desktop/wordpress.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("C:/Users/gaoxinyu/Desktop/wordpress2.txt"))
) {
byte[] bytes = new byte[1024];
int len;
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
bos.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
Object读取对象
注意写完一个对象之后要等关闭之后才能读取, 因为写完之后指针指向文件末尾
代码如下:
public class Main {
public static void main(String[] args) {
try (
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:/Users/gaoxinyu/Desktop/student.txt"))
) {
Student student = new Student("sanjin");
oos.writeObject(student);
} catch (Exception e) {
throw new RuntimeException(e);
}
try (
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:/Users/gaoxinyu/Desktop/student.txt"))
) {
Student read = (Student) ois.readObject();
System.out.println(read.getName()); // sanjin
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
class Student implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
写的是二进制数据

1.2 字符流
常用类如下

File读取文本文件
public static void main(String[] args) {
try (
BufferedReader in = new BufferedReader(new FileReader("C:/Users/gaoxinyu/Desktop/wordpress.txt"));
BufferedWriter out = new BufferedWriter(new FileWriter("C:/Users/gaoxinyu/Desktop/wordpress2.txt"))
) {
String line;
while ((line = in.readLine()) != null) {
out.write(line);
out.newLine(); // 一行写完换行
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
2. BIO、NIO、AIO
2.1 BIO
工作机制:
- 同步阻塞:每个连接都需要一个独立的线程来处理读写操作
- 当线程进行读或写操作时,如果没有数据到达或数据没有写完,线程会被阻塞,直到操作完成
优点: 简单
缺点: 低效:由于线程阻塞,每个连接都会占用一个线程,线程资源消耗大
适用场景: 应用场景连接数较少且需要简单实现的程序,例如文件传输、传统的 HTTP 服务。
示例代码:
public class BIOServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8080);
System.out.println("BIO Server started...");
while (true) {
Socket socket = serverSocket.accept(); // 阻塞等待客户端连接
new Thread(() -> {
try (InputStream inputStream = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
String line;
while ((line = reader.readLine()) != null) { // 阻塞等待数据
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
}
2.2 NIO
工作机制:
- 使用单线程管理多个连接,线程不会阻塞在某个读写操作上
- 基于通道(Channel)和缓冲区(Buffer),数据从通道读入缓冲区,或从缓冲区写入通道
- 使用 Selector 来监控多个通道的事件(如连接请求、可读、可写等)
优点:
- 单线程可以管理多个连接,资源利用率高
- 更适合高并发场景,避免了线程数与连接数直接绑定的问题
缺点: 数据处理效率依赖操作系统支持,性能未必总是优于 BIO
适用场景: 高并发、长连接的网络应用,例如即时通讯、游戏服务器
2.3 AIO
工作机制:
- 异步非阻塞:基于事件和回调机制
- 操作不需要线程阻塞,读写操作会交由操作系统完成,完成后通过回调通知应用程序
优点
- 真正的异步 I/O,效率更高
- 对编程者更友好,使用回调减少了手动处理事件的复杂性
缺点
- 依赖操作系统的异步 I/O 支持,跨平台性较差
- 适用场景较窄,不适合短连接、高吞吐的场景
适用场景
- 高吞吐、低延迟的网络应用,例如高性能的文件传输、实时系统
3. 操作系统的IO
3.1 同步阻塞
阻塞IO就是当应用A发起读取数据申请时,在内核数据没有准备好之前,应用A会一直处于等待数据状态,直到内核把数据准备好了交给应用A才结束。
3.2 同步非阻塞
非阻塞IO就是当应用A发起读取数据申请时,如果内核数据没有准备好会即刻告诉应用A(返回错误码等),不会让A在这里等待。一旦内核中的数据准备好了,并且又再次收到了A的请求,那么它马上就将数据拷贝到了用户线程,然后返回。
3.3 IO复用
由一个线程监控多个网络请求(linux系统把所有网络请求以一个fd来标识,我们后面将称为fd即文件描述符),这样就可以只需要一个或几个线程就可以完成数据状态询问的操作,当有数据准备就绪之后再分配对应的线程去读取数据,这么做就可以节省出大量的线程资源出来,这个就是IO复用模型的思路
select(时间复杂度)O(n))
MARKDOWN_HASH99938282f04071859941e18f16efcf42MARKDOWNHASH:线性轮询扫描所有的fd,不管他们是否活跃,监听的IO最大连接数不能多于FD SIZE(32位操作系统1024,64位操作系统2048
poll(时间复杂度O(n))
poll:原理和select相似,poll底层需要分配一个pollfd结构数组,维护在内核中,它没有数量限制,但IO数量大,扫描线性性能下降
epoll (时间复杂度O(1))
epoll:用于代替poll和select,没有大小限制。epoll采用事件驱动代替了轮询,epoll会把哪个流发生了怎样的I/O事件通知用户线程,所以我们说epoll实际上是事件驱动(每个事件关联上fd)的,此时用户线程对这些流的操作都是有意义的
3.4 信号驱动
3.5 异步
在异步IO模型中,应用只需要向内核发送一个请求,告诉内核它要读取数据后即刻返回;内核收到请求后会建立一个信号联系,当数据准备就绪,内核会主动把数据从内核复制到用户空间(而信号驱动是告诉应用程序何时可以开始拷贝数据),异步IO模型真正的做到了完完全全的非阻塞