Java IO相关技术小结
Java IO技术分为传统BIO(同步阻塞)、NIO(同步非阻塞)和AIO(异步非阻塞)三大类。BIO基于流操作,适合简单文件或网络通信;NIO引入通道、缓冲区和选择器,支持高并发处理;AIO由操作系统完成IO操作后通知应用。BIO线程开销大,NIO适合短连接高并发场景,AIO适合长连接任务。关键点包括:字节流与字符流的区别、NIO的零拷贝机制、Selector多路复用,以及异步回调模式。注意使用
Java IO(输入/输出)相关技术
一、Java IO基础概念
-
数据流方向
- 输入流(InputStream/Reader):从数据源(文件、网络、内存)读取数据到程序。
- 输出流(OutputStream/Writer):从程序写入数据到目标位置。
-
数据类型
- 字节流(Byte Stream):以字节为单位处理数据(
InputStream
/OutputStream
),适用于二进制文件(图片、视频)。 - 字符流(Character Stream):以字符为单位处理数据(
Reader
/Writer
),自动处理字符编码(如UTF-8),适用于文本文件。
- 字节流(Byte Stream):以字节为单位处理数据(
-
缓冲机制
- 缓冲流(BufferedStream):通过内存缓冲区减少IO操作次数,提升性能(如
BufferedInputStream
)。
- 缓冲流(BufferedStream):通过内存缓冲区减少IO操作次数,提升性能(如
二、传统IO(BIO,Blocking IO)
1. 核心类库
-
字节流
// 文件操作示例 try (FileInputStream fis = new FileInputStream("input.txt"); FileOutputStream fos = new FileOutputStream("output.txt")) { int data; while ((data = fis.read()) != -1) { fos.write(data); } } catch (IOException e) { e.printStackTrace(); }
- 常用类:
FileInputStream
、FileOutputStream
、ByteArrayInputStream
、DataInputStream
。
- 常用类:
-
字符流
// 文本文件读写示例 try (BufferedReader br = new BufferedReader(new FileReader("input.txt")); BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"))) { String line; while ((line = br.readLine()) != null) { bw.write(line); bw.newLine(); } } catch (IOException e) { e.printStackTrace(); }
- 常用类:
FileReader
、FileWriter
、BufferedReader
、PrintWriter
。
- 常用类:
2. 装饰器模式
Java IO通过装饰器模式组合功能(如BufferedInputStream
包装FileInputStream
):
InputStream is = new BufferedInputStream(new FileInputStream("data.bin"));
3. 典型应用场景
- 简单文件读写(配置文件、日志)。
- 网络通信(Socket输入输出)。
三、NIO(New IO,JDK 1.4引入)
1. 核心特性
- 非阻塞IO:基于通道(
Channel
)和缓冲区(Buffer
),支持单线程处理多个连接(Selector模式)。 - 零拷贝(Zero-copy):直接在内核空间完成数据传输,减少用户空间与内核空间的切换。
2. 核心组件
-
Buffer
用于与Channel交互的数据容器,支持ByteBuffer
、CharBuffer
等。ByteBuffer buffer = ByteBuffer.allocate(1024); // 分配堆内存缓冲区 buffer.put("Hello".getBytes()); // 写入数据 buffer.flip(); // 切换为读模式 while (buffer.hasRemaining()) { System.out.print((char) buffer.get()); }
-
Channel
双向数据通道,支持异步读写,如:// 文件Channel示例 try (FileChannel channel = new FileInputStream("input.txt").getChannel()) { ByteBuffer buffer = ByteBuffer.allocate(1024); int bytesRead = channel.read(buffer); // 从通道读取到缓冲区 } catch (IOException e) { e.printStackTrace(); }
-
Selector
实现单线程管理多个通道的事件(连接就绪、可读、可写):Selector selector = Selector.open(); channel.configureBlocking(false); // 设置非阻塞模式 channel.register(selector, SelectionKey.OP_READ); // 注册读事件 while (selector.select() > 0) { Set<SelectionKey> keys = selector.selectedKeys(); Iterator<SelectionKey> it = keys.iterator(); while (it.hasNext()) { SelectionKey key = it.next(); if (key.isReadable()) { // 处理读事件 } it.remove(); } }
3. 应用场景
- 高并发网络编程(如Netty框架基于NIO实现)。
- 文件大文件处理(零拷贝提升性能)。
四、AIO(Asynchronous IO,JDK 7引入)
1. 核心特性
- 异步非阻塞:基于回调或Future模式,IO操作完成后通知应用程序。
- 真正的异步:由操作系统完成IO操作,应用程序无需等待。
2. 核心类库
AsynchronousFileChannel
:异步文件操作。AsynchronousSocketChannel
:异步网络通信。
// 异步文件读取示例
AsynchronousFileChannel channel = AsynchronousFileChannel.open(
Paths.get("data.txt"), StandardOpenOption.READ);
ByteBuffer buffer = ByteBuffer.allocate(1024);
channel.read(buffer, 0, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
System.out.println("Read " + result + " bytes");
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
exc.printStackTrace();
}
});
3. 应用场景
- 异步网络服务(如高性能服务器)。
- 实时数据处理(如日志收集、消息队列)。
五、IO与NIO/AIO对比
特性 | BIO(传统IO) | NIO(非阻塞IO) | AIO(异步IO) |
---|---|---|---|
阻塞模式 | 同步阻塞 | 同步非阻塞 | 异步非阻塞 |
核心组件 | 流(Stream) | 通道(Channel)、缓冲区(Buffer)、选择器(Selector) | 异步通道(AsynchronousChannel) |
线程模型 | 一个连接一个线程 | 一个线程处理多个连接 | 线程池处理回调 |
适用场景 | 连接数少且固定的场景 | 连接数多但短操作的场景(如Web服务器) | 连接数多且长操作的场景(如文件传输) |
六、典型应用场景
-
文件操作
- 小文件读写:使用
BufferedReader
/BufferedWriter
(字符流)。 - 大文件读写:使用
FileChannel
+ByteBuffer
(NIO零拷贝)。
- 小文件读写:使用
-
网络编程
- 低并发:传统Socket+
InputStream
/OutputStream
。 - 高并发:NIO的
Selector
+SocketChannel
(如Netty框架)。 - 异步通信:AIO的
AsynchronousSocketChannel
(如JDK自带的AsynchronousServerSocketChannel
)。
- 低并发:传统Socket+
-
序列化与反序列化
- 使用
ObjectInputStream
/ObjectOutputStream
实现Java对象的序列化。
- 使用
七、注意事项
-
资源管理
- 使用
try-with-resources
语句自动关闭流,避免内存泄漏。
- 使用
-
字符编码
- 处理文本时显式指定编码(如
new FileReader("file.txt", StandardCharsets.UTF_8)
),避免乱码。
- 处理文本时显式指定编码(如
-
性能优化
- 优先使用缓冲流(
BufferedInputStream
等)减少IO次数。 - 大文件读写时使用NIO的
FileChannel
替代传统流。
- 优先使用缓冲流(
-
异常处理
- 捕获
IOException
及其子类,处理连接断开、文件不存在等异常。
- 捕获
八、相关框架与工具
- Netty:基于NIO的高性能网络编程框架,广泛用于分布式系统通信。
- Apache Commons IO:提供更简洁的IO操作工具类(如
FileUtils
)。 - Google Guava:增强的IO工具类(如
Files
、ByteStreams
)。
通过合理选择IO技术(BIO/NIO/AIO)和工具类库,可显著提升Java应用的性能和可维护性。
常用类库及工具
一、文件与路径操作类库
1. java.nio.file
包(JDK 7+)
-
核心类:
Path
:表示文件或目录的路径(替代File
类)。Paths
:工厂类,用于创建Path
实例。Files
:工具类,提供文件操作静态方法(读写、复制、删除等)。
// 示例:递归复制目录 Path source = Paths.get("src"); Path target = Paths.get("dst"); Files.walk(source) .forEach(sourcePath -> { try { Path targetPath = target.resolve(source.relativize(sourcePath)); Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { e.printStackTrace(); } });
2. java.io.File
(传统文件操作)
- 常用方法:
File file = new File("example.txt"); file.exists(); // 判断文件是否存在 file.isDirectory(); // 是否为目录 file.listFiles(); // 获取目录下的文件列表 file.createNewFile(); // 创建新文件
二、序列化与反序列化
1. 标准Java序列化
-
核心接口:
Serializable
:标记接口,实现该接口的类可被序列化。Externalizable
:自定义序列化逻辑(需实现writeExternal()
和readExternal()
方法)。
// 序列化示例 try (ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("data.ser"))) { oos.writeObject(new User("Alice", 25)); } catch (IOException e) { e.printStackTrace(); } // 反序列化示例 try (ObjectInputStream ois = new ObjectInputStream( new FileInputStream("data.ser"))) { User user = (User) ois.readObject(); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); }
2. 第三方序列化库
-
JSON序列化:Jackson、Gson
// Jackson示例 ObjectMapper mapper = new ObjectMapper(); User user = new User("Bob", 30); String json = mapper.writeValueAsString(user); // 对象转JSON User deserializedUser = mapper.readValue(json, User.class); // JSON转对象
-
高性能序列化:Kryo、Protostuff(基于Protobuf)。
三、压缩与归档
1. java.util.zip
包
-
常用类:
ZipInputStream
/ZipOutputStream
:读写ZIP文件。GZIPInputStream
/GZIPOutputStream
:读写GZIP压缩文件。
// ZIP压缩示例 try (ZipOutputStream zos = new ZipOutputStream( new FileOutputStream("archive.zip"))) { File file = new File("data.txt"); zos.putNextEntry(new ZipEntry(file.getName())); try (FileInputStream fis = new FileInputStream(file)) { byte[] buffer = new byte[1024]; int length; while ((length = fis.read(buffer)) > 0) { zos.write(buffer, 0, length); } } zos.closeEntry(); } catch (IOException e) { e.printStackTrace(); }
2. Apache Commons Compress
- 支持更多格式:ZIP、TAR、BZIP2、7-Zip等。
// TAR文件操作示例 try (TarArchiveOutputStream taos = new TarArchiveOutputStream( new FileOutputStream("archive.tar"))) { TarArchiveEntry entry = new TarArchiveEntry(new File("data.txt")); taos.putArchiveEntry(entry); try (FileInputStream fis = new FileInputStream("data.txt")) { IOUtils.copy(fis, taos); } taos.closeArchiveEntry(); } catch (IOException e) { e.printStackTrace(); }
四、网络编程类库
1. 标准Socket编程
-
TCP通信:
Socket
(客户端)和ServerSocket
(服务器)。// 服务器端示例 try (ServerSocket serverSocket = new ServerSocket(8080)) { Socket clientSocket = serverSocket.accept(); try (BufferedReader in = new BufferedReader( new InputStreamReader(clientSocket.getInputStream())); PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true)) { String inputLine; while ((inputLine = in.readLine()) != null) { out.println("Server received: " + inputLine); } } } catch (IOException e) { e.printStackTrace(); }
-
UDP通信:
DatagramSocket
和DatagramPacket
。
2. HTTP客户端
-
JDK 11+ HttpClient(替代
HttpURLConnection
):HttpClient client = HttpClient.newBuilder().build(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.example.com/data")) .GET() .build(); HttpResponse<String> response = client.send(request, BodyHandlers.ofString()); System.out.println(response.body());
-
Apache HttpClient:功能更丰富,支持连接池、代理等。
五、管道与内存流
1. 管道流(Piped Streams)
- 实现线程间通信:
// 发送线程 PipedOutputStream pos = new PipedOutputStream(); writerThread = new Thread(() -> { pos.write("Hello, Pipe!".getBytes()); pos.close(); }); // 接收线程 PipedInputStream pis = new PipedInputStream(pos); readerThread = new Thread(() -> { int data; while ((data = pis.read()) != -1) { System.out.print((char) data); } pis.close(); });
2. 内存流
ByteArrayInputStream
/ByteArrayOutputStream
:在内存中读写数据。ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write("Hello, Memory!".getBytes()); byte[] bytes = baos.toByteArray(); ByteArrayInputStream bais = new ByteArrayInputStream(bytes); int data; while ((data = bais.read()) != -1) { System.out.print((char) data); }
六、字符编码转换
1. java.nio.charset
包
-
常用类:
Charset
:表示字符集(如UTF-8、GBK)。CharsetEncoder
/CharsetDecoder
:实现字符与字节的转换。
// 字符串编码转换示例 String text = "你好,世界"; Charset utf8 = Charset.forName("UTF-8"); Charset gbk = Charset.forName("GBK"); ByteBuffer utf8Bytes = utf8.encode(text); CharBuffer gbkChars = gbk.decode(utf8Bytes); String convertedText = gbkChars.toString();
七、第三方工具类库
1. Apache Commons IO
-
常用工具类:
FileUtils
:文件操作(读取、写入、复制)。IOUtils
:流操作(关闭、复制、转换为字符串)。
// 示例:读取文件内容为字符串 String content = FileUtils.readFileToString(new File("data.txt"), StandardCharsets.UTF_8); // 示例:复制输入流到输出流 IOUtils.copy(inputStream, outputStream);
2. Google Guava
-
核心类:
Files
:文件操作(读取、写入、临时文件)。ByteStreams
/CharStreams
:字节流/字符流操作。
// 示例:写入字符串到文件 Files.asCharSink(new File("output.txt"), StandardCharsets.UTF_8).write("Hello, Guava!"); // 示例:读取文件所有行 List<String> lines = Files.asCharSource(new File("input.txt"), StandardCharsets.UTF_8).readLines();
八、NIO.2(JDK 7+)增强功能
1. 异步文件操作
AsynchronousFileChannel
支持回调或Future模式:AsynchronousFileChannel channel = AsynchronousFileChannel.open( Paths.get("data.txt"), StandardOpenOption.READ); ByteBuffer buffer = ByteBuffer.allocate(1024); Future<Integer> result = channel.read(buffer, 0); while (!result.isDone()) { // 处理其他任务 } int bytesRead = result.get();
2. 文件监听(WatchService)
- 监控文件系统变化(创建、修改、删除):
WatchService watcher = FileSystems.getDefault().newWatchService(); Path dir = Paths.get("."); dir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY); WatchKey key; while ((key = watcher.take()) != null) { for (WatchEvent<?> event : key.pollEvents()) { System.out.println("Event kind:" + event.kind() + " Path : " + event.context()); } key.reset(); }
九、总结
Java IO体系涵盖传统BIO、NIO、AIO及多种工具类库,选择时需根据场景权衡:
- 简单文件操作:优先使用
java.nio.file.Files
或Apache Commons IO。 - 网络编程:低并发用
Socket
,高并发用Netty(基于NIO)。 - 序列化:标准场景用Java序列化,高性能场景用Kryo或Protobuf。
- 压缩:ZIP/GZIP用JDK原生类,复杂格式用Apache Commons Compress。
合理结合JDK原生类库与第三方工具,可大幅提升IO操作的效率和代码简洁性。
序列化类库
一、Java原生序列化(JDK内置)
1. 实现步骤
- 让类实现
Serializable
接口(标记接口,无需实现方法)。 - 使用
ObjectOutputStream
序列化对象。 - 使用
ObjectInputStream
反序列化对象。
2. 示例代码
import java.io.*;
// 1. 定义可序列化的类
class User implements Serializable {
private static final long serialVersionUID = 1L; // 序列化版本号
private String name;
private int age;
// transient字段不会被序列化
private transient String password;
public User(String name, int age, String password) {
this.name = name;
this.age = age;
this.password = password;
}
// Getters and setters
public String getName() { return name; }
public int getAge() { return age; }
public String getPassword() { return password; }
}
public class SerializationExample {
public static void main(String[] args) {
User user = new User("Alice", 30, "securePass");
// 2. 序列化对象到文件
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("user.ser"))) {
oos.writeObject(user);
System.out.println("对象已序列化");
} catch (IOException e) {
e.printStackTrace();
}
// 3. 从文件反序列化对象
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("user.ser"))) {
User deserializedUser = (User) ois.readObject();
System.out.println("反序列化结果:");
System.out.println("Name: " + deserializedUser.getName());
System.out.println("Age: " + deserializedUser.getAge());
System.out.println("Password: " + deserializedUser.getPassword()); // null(transient字段未被序列化)
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
3. 关键特性
serialVersionUID
:版本控制,建议显式声明以避免反序列化时的InvalidClassException
。transient
关键字:标记不需要序列化的字段(如密码、临时数据)。- 自动处理引用关系:若对象包含其他对象的引用,这些对象也需实现
Serializable
。
二、JSON序列化(Jackson/Gson)
1. Jackson示例(需添加依赖)
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.2</version>
</dependency>
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonExample {
public static void main(String[] args) throws Exception {
User user = new User("Bob", 25, "pass123");
ObjectMapper mapper = new ObjectMapper();
// 1. 对象转JSON字符串
String json = mapper.writeValueAsString(user);
System.out.println("JSON: " + json);
// 2. JSON字符串转对象
User deserializedUser = mapper.readValue(json, User.class);
System.out.println("Name: " + deserializedUser.getName());
}
}
2. Gson示例(需添加依赖)
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
import com.google.gson.Gson;
public class GsonExample {
public static void main(String[] args) {
User user = new User("Charlie", 35, "secure123");
Gson gson = new Gson();
// 1. 对象转JSON
String json = gson.toJson(user);
System.out.println("JSON: " + json);
// 2. JSON转对象
User deserializedUser = gson.fromJson(json, User.class);
System.out.println("Age: " + deserializedUser.getAge());
}
}
三、高性能序列化(Kryo)
1. 添加依赖
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>5.4.0</version>
</dependency>
2. 示例代码
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class KryoExample {
public static void main(String[] args) {
User user = new User("David", 40, "kryoPass");
Kryo kryo = new Kryo();
kryo.register(User.class);
// 1. 序列化
try (Output output = new Output(new FileOutputStream("user.kryo"))) {
kryo.writeObject(output, user);
System.out.println("Kryo序列化完成");
} catch (IOException e) {
e.printStackTrace();
}
// 2. 反序列化
try (Input input = new Input(new FileInputStream("user.kryo"))) {
User deserializedUser = kryo.readObject(input, User.class);
System.out.println("Name: " + deserializedUser.getName());
} catch (IOException e) {
e.printStackTrace();
}
}
}
四、Protobuf(Google Protocol Buffers)
1. 添加依赖
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.23.4</version>
</dependency>
2. 定义.proto
文件
syntax = "proto3";
package example;
message User {
string name = 1;
int32 age = 2;
string password = 3;
}
3. 生成Java类并使用
import com.google.protobuf.InvalidProtocolBufferException;
public class ProtobufExample {
public static void main(String[] args) {
// 1. 创建对象并序列化
UserProto.User user = UserProto.User.newBuilder()
.setName("Eve")
.setAge(28)
.setPassword("protoPass")
.build();
byte[] bytes = user.toByteArray();
// 2. 反序列化
try {
UserProto.User deserializedUser = UserProto.User.parseFrom(bytes);
System.out.println("Age: " + deserializedUser.getAge());
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
}
}
五、对比与选择建议
序列化方式 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
Java原生 | 无需额外依赖,支持复杂对象结构 | 性能较差,字节码大,兼容性差 | 简单内部系统,对象结构稳定 |
JSON(Jackson) | 可读性强,跨语言支持,灵活 | 性能一般,字节码较大 | 前后端交互,REST API |
Kryo | 高性能,字节码小 | 不跨语言,需注册类 | 内部系统高性能场景(如缓存) |
Protobuf | 极致性能,跨语言,强兼容性 | 需要编写.proto 文件,学习成本较高 |
分布式系统,微服务间通信 |
六、注意事项
- 版本兼容性:Java原生序列化需通过
serialVersionUID
控制版本。 - 安全性:避免反序列化不可信来源的数据(可能导致代码注入攻击)。
- 性能优化:对性能敏感的场景,优先选择Kryo或Protobuf。
- 字段控制:使用
transient
或Jackson的@JsonIgnore
排除不需要序列化的字段。
根据场景选择合适的序列化方式,可显著提升系统的性能和可维护性。

GitCode 天启AI是一款由 GitCode 团队打造的智能助手,基于先进的LLM(大语言模型)与多智能体 Agent 技术构建,致力于为用户提供高效、智能、多模态的创作与开发支持。它不仅支持自然语言对话,还具备处理文件、生成 PPT、撰写分析报告、开发 Web 应用等多项能力,真正做到“一句话,让 Al帮你完成复杂任务”。
更多推荐
所有评论(0)