NIO
NIO概述
NIO(New IO)是Java 1.4引入的非阻塞IO,提供了更高效的IO操作。
NIO vs IO
| 特性 | IO | NIO |
|---|---|---|
| 方式 | 面向流 | 面向缓冲区 |
| 阻塞 | 阻塞IO | 非阻塞IO |
| 选择器 | 无 | Selector支持 |
Buffer
Buffer类型
- ByteBuffer
- CharBuffer
- ShortBuffer
- IntBuffer
- LongBuffer
- FloatBuffer
- DoubleBuffer
Buffer核心属性
java
public class BufferDemo {
public static void main(String[] args) {
// 创建Buffer
ByteBuffer buffer = ByteBuffer.allocate(10);
System.out.println("初始状态:");
System.out.println("capacity: " + buffer.capacity()); // 10
System.out.println("limit: " + buffer.limit()); // 10
System.out.println("position: " + buffer.position()); // 0
// 写入数据
buffer.put((byte) 'H');
buffer.put((byte) 'e');
buffer.put((byte) 'l');
buffer.put((byte) 'l');
buffer.put((byte) 'o');
System.out.println("\n写入数据后:");
System.out.println("position: " + buffer.position()); // 5
// 切换为读模式
buffer.flip();
System.out.println("\nflip后:");
System.out.println("limit: " + buffer.limit()); // 5
System.out.println("position: " + buffer.position()); // 0
// 读取数据
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}
System.out.println("\n\n读取后:");
System.out.println("position: " + buffer.position()); // 5
// 重置position
buffer.rewind();
System.out.println("rewind后 position: " + buffer.position()); // 0
// 清空buffer
buffer.clear();
System.out.println("clear后 position: " + buffer.position()); // 0
}
}Buffer方法
| 方法 | 说明 |
|---|---|
allocate(int capacity) | 分配缓冲区 |
put() | 写入数据 |
get() | 读取数据 |
flip() | 切换为读模式 |
rewind() | 重置position为0 |
clear() | 清空缓冲区 |
compact() | 压缩缓冲区 |
mark() | 标记当前位置 |
reset() | 恢复到mark位置 |
直接缓冲区
java
// 非直接缓冲区(堆内存)
ByteBuffer heapBuffer = ByteBuffer.allocate(1024);
// 直接缓冲区(操作系统内存)
ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024);Channel
FileChannel
java
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class FileChannelDemo {
public static void main(String[] args) {
// 写入文件
try (FileChannel channel = new FileOutputStream("test.txt").getChannel()) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put("Hello, NIO!".getBytes());
buffer.flip();
channel.write(buffer);
} catch (IOException e) {
e.printStackTrace();
}
// 读取文件
try (FileChannel channel = new FileInputStream("test.txt").getChannel()) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = channel.read(buffer);
buffer.flip();
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}
} catch (IOException e) {
e.printStackTrace();
}
// 复制文件
try (
FileChannel srcChannel = new FileInputStream("source.txt").getChannel();
FileChannel destChannel = new FileOutputStream("target.txt").getChannel()
) {
// 方式1:使用transferTo
srcChannel.transferTo(0, srcChannel.size(), destChannel);
// 方式2:使用transferFrom
// destChannel.transferFrom(srcChannel, 0, srcChannel.size());
} catch (IOException e) {
e.printStackTrace();
}
}
}FileChannel读写文件
java
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class RandomAccessDemo {
public static void main(String[] args) {
try (RandomAccessFile file = new RandomAccessFile("test.txt", "rw");
FileChannel channel = file.getChannel()) {
// 写入数据
ByteBuffer buffer = ByteBuffer.allocate(1024);
String data = "Hello, FileChannel!";
buffer.put(data.getBytes());
buffer.flip();
channel.write(buffer);
// 读取数据
buffer.clear();
channel.position(0); // 移动到文件开头
int bytesRead = channel.read(buffer);
buffer.flip();
System.out.println(new String(buffer.array(), 0, bytesRead));
// 获取文件大小
long fileSize = channel.size();
System.out.println("文件大小: " + fileSize);
} catch (IOException e) {
e.printStackTrace();
}
}
}Selector
Selector用于实现非阻塞IO的多路复用。
服务端示例
java
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
public class NioServer {
public static void main(String[] args) throws IOException {
// 创建Selector
Selector selector = Selector.open();
// 创建ServerSocketChannel
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress(8080));
serverChannel.configureBlocking(false); // 非阻塞模式
// 注册到Selector,监听连接事件
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服务器启动,监听端口8080...");
while (true) {
// 阻塞等待就绪的通道
selector.select();
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectedKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (key.isAcceptable()) {
// 处理连接
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel client = server.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
System.out.println("客户端连接: " + client.getRemoteAddress());
} else if (key.isReadable()) {
// 处理读取
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = client.read(buffer);
if (bytesRead == -1) {
client.close();
System.out.println("客户端断开");
} else {
buffer.flip();
String message = new String(buffer.array(), 0, bytesRead);
System.out.println("收到消息: " + message);
// 回显
buffer.rewind();
client.write(buffer);
}
}
}
}
}
}客户端示例
java
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Scanner;
public class NioClient {
public static void main(String[] args) throws IOException {
SocketChannel channel = SocketChannel.open();
channel.configureBlocking(false);
channel.connect(new InetSocketAddress("localhost", 8080));
// 等待连接完成
while (!channel.finishConnect()) {
System.out.println("连接中...");
}
System.out.println("连接成功");
Scanner scanner = new Scanner(System.in);
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (true) {
System.out.print("输入消息: ");
String message = scanner.nextLine();
if ("exit".equals(message)) {
break;
}
// 发送消息
buffer.clear();
buffer.put(message.getBytes());
buffer.flip();
channel.write(buffer);
// 接收响应
buffer.clear();
int bytesRead = channel.read(buffer);
if (bytesRead > 0) {
buffer.flip();
String response = new String(buffer.array(), 0, bytesRead);
System.out.println("服务器响应: " + response);
}
}
channel.close();
}
}Path和Files
Path类(Java 7+)
java
import java.nio.file.*;
public class PathDemo {
public static void main(String[] args) {
// 创建Path
Path path = Paths.get("test.txt");
Path path2 = Paths.get("dir", "subdir", "file.txt");
Path path3 = Path.of("test.txt"); // Java 11+
// Path信息
System.out.println("文件名: " + path.getFileName());
System.out.println("父目录: " + path.getParent());
System.out.println("根目录: " + path.getRoot());
System.out.println("绝对路径: " + path.toAbsolutePath());
// 路径操作
Path normalized = Paths.get("dir/../file.txt").normalize();
System.out.println("规范化: " + normalized);
Path resolved = Paths.get("dir").resolve("file.txt");
System.out.println("拼接: " + resolved);
Path relative = Paths.get("dir/subdir/file.txt").relativize(Paths.get("dir/other.txt"));
System.out.println("相对路径: " + relative);
}
}Files类
java
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.util.List;
public class FilesDemo {
public static void main(String[] args) throws Exception {
Path path = Paths.get("test.txt");
// 创建文件
Files.createFile(path);
// 创建目录
Files.createDirectories(Paths.get("dir/subdir"));
// 写入文件
Files.writeString(path, "Hello, Files!", StandardCharsets.UTF_8);
// 读取文件
String content = Files.readString(path);
System.out.println(content);
// 读取所有行
List<String> lines = Files.readAllLines(path);
lines.forEach(System.out::println);
// 读取所有字节
byte[] bytes = Files.readAllBytes(path);
// 复制文件
Files.copy(path, Paths.get("copy.txt"), StandardCopyOption.REPLACE_EXISTING);
// 移动文件
Files.move(path, Paths.get("moved.txt"), StandardCopyOption.REPLACE_EXISTING);
// 删除文件
Files.deleteIfExists(Paths.get("moved.txt"));
// 文件信息
System.out.println("大小: " + Files.size(path));
System.out.println("是否文件: " + Files.isRegularFile(path));
System.out.println("是否目录: " + Files.isDirectory(path));
System.out.println("是否隐藏: " + Files.isHidden(path));
// 遍历目录
Files.walk(Paths.get("."))
.filter(Files::isRegularFile)
.forEach(System.out::println);
// 查找文件
Files.find(Paths.get("."), 10,
(p, attr) -> p.toString().endsWith(".java"))
.forEach(System.out::println);
}
}NIO vs IO选择
| 场景 | 推荐 |
|---|---|
| 少量连接 | 传统IO |
| 大量连接 | NIO + Selector |
| 文件复制 | FileChannel.transferTo |
| 文件读写 | Files类(Java 7+) |
| 高性能网络 | Netty框架 |