Java IO相关技术小结
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>() {@Overridepublic void completed(Integer result, ByteBuffer attachment) {System.out.println("Read " + result + " bytes");}@Overridepublic 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 setterspublic 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. 对象转JSONString 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
排除不需要序列化的字段。
根据场景选择合适的序列化方式,可显著提升系统的性能和可维护性。