“在编程的世界里,数据就像水流一样,输入输出流则是连接程序与外部世界的管道。”
Java 的输入输出流(IO)是 Java 编程中不可或缺的一部分,无论是读取文件、处理网络数据,还是与用户交互,IO 都扮演着至关重要的角色。本文将带你深入理解 Java 的 IO 体系,从基础的输入输出流到高级的 NIO 和异步 IO 编程模型,帮助你掌握这一核心技能。
CompletableFuture
。Java 中的输入输出流是用来处理数据输入和输出的抽象概念。流可以看作是数据的流动,输入流用于读取数据,输出流用于写入数据。
按方向分:
按数据类型分:
流类型对比表
特征 | 字节流 | 字符流 |
---|---|---|
数据单位 | 8位字节 | 16位Unicode |
典型类 | FileInputStream | FileReader |
适用场景 | 二进制文件 | 文本文件 |
默认编码支持 | 无 | 支持(UTF-8等) |
接口/类 | 描述 |
---|---|
InputStream |
所有字节输入流的超类 |
OutputStream |
所有字节输出流的超类 |
Reader |
所有字符输入流的超类 |
Writer |
所有字符输出流的超类 |
FileInputStream |
用于从文件中读取字节 |
FileOutputStream |
用于将字节写入文件 |
FileReader |
用于从文件中读取字符 |
FileWriter |
用于将字符写入文件 |
InputStream
常用方法方法名 | 描述 |
---|---|
int read() |
读取一个字节的数据,返回读取的字节数 |
int read(byte[] b) |
读取多个字节到字节数组 b 中 |
void close() |
关闭输入流,释放资源 |
OutputStream
常用方法方法名 | 描述 |
---|---|
void write(int b) |
写入一个字节的数据 |
void write(byte[] b) |
写入字节数组 b 中的数据 |
void close() |
关闭输出流,释放资源 |
装饰器模式(Decorator Pattern)是 Java IO 体系中的核心设计模式之一。它允许在不改变原有对象的情况下,动态地扩展其功能。
装饰器模式结构
// 目录结构
src
└── main
└── java
└── com
└── example
└── io
└── DecoratorExample.java
// DecoratorExample.java
package com.example.io;
import java.io.*;
public class DecoratorExample {
public static void main(String[] args) {
try (InputStream in = new BufferedInputStream(new FileInputStream("input.txt"))) {
int data;
while ((data = in.read()) != -1) {
System.out.print((char) data);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
代码解释:
FileInputStream
是基础的字节输入流,用于从文件中读取数据。BufferedInputStream
是装饰器,它在 FileInputStream
的基础上增加了缓冲功能,提高了读取效率。NIO(New IO)是 Java 1.4 引入的新 IO API,提供了更高效的 IO 操作方式。NIO 的核心组件包括 Buffer
、Channel
和 Selector
。
Buffer
是 NIO 中用于存储数据的容器,它是一个线性的、有限的数据结构。Buffer
有多个子类,如 ByteBuffer
、CharBuffer
等。
// 目录结构
src
└── main
└── java
└── com
└── example
└── nio
└── BufferExample.java
// BufferExample.java
package com.example.nio;
import java.nio.ByteBuffer;
public class BufferExample {
public static void main(String[] args) {
ByteBuffer buffer = ByteBuffer.allocate(1024); // 分配一个 1024 字节的缓冲区
buffer.put((byte) 'H'); // 写入数据
buffer.put((byte) 'e');
buffer.put((byte) 'l');
buffer.put((byte) 'l');
buffer.put((byte) 'o');
buffer.flip(); // 切换为读模式
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get()); // 读取数据
}
}
}
代码解释:
ByteBuffer.allocate(1024)
:分配一个 1024 字节的缓冲区。buffer.put()
:向缓冲区写入数据。buffer.flip()
:切换缓冲区的模式,从写模式切换到读模式。buffer.get()
:从缓冲区读取数据。状态 | 描述 |
---|---|
写模式 | 数据可以被写入缓冲区 |
读模式 | 数据可以从缓冲区读取 |
清空模式 | 清空缓冲区,准备重新写入数据 |
零拷贝(Zero-copy)是一种优化技术,旨在减少数据在内存中的拷贝次数,从而提高 IO 操作的效率。
操作 | 传统 IO | NIO(零拷贝) |
---|---|---|
数据拷贝次数 | 多次拷贝(用户空间与内核空间之间) | 零拷贝或一次拷贝 |
性能 | 较低 | 较高 |
// 目录结构
src
└── main
└── java
└── com
└── example
└── nio
└── ZeroCopyExample.java
// ZeroCopyExample.java
package com.example.nio;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.channels.FileChannel;
public class ZeroCopyExample {
public static void main(String[] args) {
try (FileInputStream fis = new FileInputStream("input.txt");
FileOutputStream fos = new FileOutputStream("output.txt");
FileChannel sourceChannel = fis.getChannel();
FileChannel destChannel = fos.getChannel()) {
sourceChannel.transferTo(0, sourceChannel.size(), destChannel);
} catch (Exception e) {
e.printStackTrace();
}
}
}
代码解释:
FileChannel.transferTo()
:将数据从源通道传输到目标通道,实现了零拷贝。文件锁(File Lock)是一种机制,用于控制多个进程或线程对同一文件的并发访问。
// 目录结构
src
└── main
└── java
└── com
└── example
└── nio
└── FileLockExample.java
// FileLockExample.java
package com.example.nio;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
public class FileLockExample {
public static void main(String[] args) {
try (RandomAccessFile file = new RandomAccessFile("test.txt", "rw");
FileChannel channel = file.getChannel()) {
FileLock lock = channel.lock(); // 获取文件锁
System.out.println("File locked");
// 模拟文件操作
Thread.sleep(5000);
lock.release(); // 释放文件锁
System.out.println("File unlocked");
} catch (Exception e) {
e.printStackTrace();
}
}
}
代码解释:
FileChannel.lock()
:获取文件锁,防止其他进程或线程访问该文件。lock.release()
:释放文件锁,允许其他进程或线程访问该文件。内存映射文件(Memory-mapped File)是一种将文件直接映射到内存的技术,允许程序像访问内存一样访问文件。
// 目录结构
src
└── main
└── java
└── com
└── example
└── nio
└── MappedFileExample.java
// MappedFileExample.java
package com.example.nio;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
public class MappedFileExample {
public static void main(String[] args) {
try (RandomAccessFile file = new RandomAccessFile("test.txt", "rw");
FileChannel channel = file.getChannel()) {
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, channel.size());
// 读取文件内容
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}
// 写入文件内容
buffer.putChar('!');
} catch (Exception e) {
e.printStackTrace();
}
}
}
代码解释:
FileChannel.map()
:将文件映射到内存中,返回一个 MappedByteBuffer
。buffer.get()
:从内存映射文件中读取数据。buffer.putChar()
:向内存映射文件中写入数据。异步 IO(Asynchronous IO)是一种非阻塞的 IO 操作方式,允许程序在等待 IO 操作完成的同时继续执行其他任务。
CompletableFuture
是 Java 8 引入的一个类,用于处理异步编程。它可以与 NIO 的异步 IO 操作结合使用,实现高效的异步编程模型。
// 目录结构
src
└── main
└── java
└── com
└── example
└── nio
└── AsyncIOExample.java
// AsyncIOExample.java
package com.example.nio;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
public class AsyncIOExample {
public static void main(String[] args) {
try (AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(
Paths.get("test.txt"), StandardOpenOption.READ)) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
Future<Integer> operation = fileChannel.read(buffer, 0);
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
try {
return operation.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
});
future.thenAccept(bytesRead -> {
System.out.println("Bytes read: " + bytesRead);
buffer.flip();
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}
}).join();
} catch (Exception e) {
e.printStackTrace();
}
}
}
代码解释:
AsynchronousFileChannel.open()
:打开一个异步文件通道。fileChannel.read()
:异步读取文件内容。CompletableFuture.supplyAsync()
:将异步操作封装为 CompletableFuture
。future.thenAccept()
:在异步操作完成后执行回调函数。本文详细介绍了 Java 的输入输出流体系,从基础的字节流和字符流到高级的 NIO 和异步 IO 编程模型。通过多个实战案例,我们深入理解了装饰器模式、Buffer 机制、零拷贝原理、文件锁、内存映射文件以及异步 IO 编程模型。
BufferedInputStream
和 BufferedOutputStream
实现文件的复制。FileChannel
和 MappedByteBuffer
实现一个简单的文件加密程序。CompletableFuture
实现一个异步文件下载器。try-with-resources
语句确保资源被正确关闭。BufferedInputStream
和 BufferedOutputStream
提高 IO 性能。NIO
的 Buffer
和 Channel
实现高效的 IO 操作。“掌握 Java 的输入输出流,就像掌握了数据流动的钥匙,打开了通往高效编程的大门。” —— 凌云
希望本文能帮助你深入理解 Java 的输入输出流体系,并在实际开发中灵活运用这些知识。如果你有任何问题或建议,欢迎在评论区留言讨论。
分享到:分享到知乎 | 分享到掘金 | 分享到微博 | 分享到 QQ | 分享到 Stack Overflow
话题标签:#Java输入输出流 #NIO #异步IO #CompletableFuture #文件锁 #内存映射文件
期待与你相遇!
如果你对编程充满热情,想获取丰富的编程学习资料,如经典编程书籍、实用教程等,欢迎加入我们的大家庭!点击云盘链接获取入群方式,扫码添加入群,即可领取海量编程资源!一起提升技能,开启你的编程进阶之旅!
上一篇:Java 泛型编程
下一篇:Java 输入输出流使用详解