Java I/O 和 NIO 缓冲区使用方式的不同
- 缓冲区类型
- Java I/O:没有专门的缓冲区概念,通过字节数组(
byte[]
)来临时存储数据。例如在 InputStream
的 read(byte[] b)
方法中,将数据读取到字节数组 b
中。
- Java NIO:引入了专门的缓冲区类,如
ByteBuffer
、CharBuffer
等。这些缓冲区类有自己的状态属性,如容量(capacity)、位置(position)和界限(limit)。例如创建一个 ByteBuffer
可以使用 ByteBuffer.allocate(1024)
,这里 1024
是缓冲区的容量。
- 数据处理方式
- Java I/O:是面向流的,数据以连续的字节流形式从数据源流向程序。读取数据时,通常是按顺序从流中读取数据填充到字节数组,写入数据时也是按顺序将字节数组的数据写入流中。例如:
InputStream inputStream = new FileInputStream("test.txt");
byte[] buffer = new byte[1024];
int bytesRead = inputStream.read(buffer);
- **Java NIO**:是面向缓冲区的,数据先被读入缓冲区,然后程序从缓冲区中处理数据。写入数据时也是先将数据放入缓冲区,再将缓冲区数据写入通道。例如:
FileChannel channel = new FileInputStream("test.txt").getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
channel.read(buffer);
buffer.flip();
while (buffer.hasRemaining()) {
byte b = buffer.get();
}
- 读写操作
- Java I/O:
read()
和 write()
方法是阻塞式的。当调用 read()
方法时,线程会阻塞直到有数据可读;调用 write()
方法时,线程会阻塞直到数据完全写入。
- Java NIO:读写操作基于通道(
Channel
),通道可以是阻塞或非阻塞的。在非阻塞模式下,read()
和 write()
方法不会阻塞线程,而是立即返回,通过检查返回值判断操作是否完成。例如在非阻塞的 SocketChannel
中:
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
ByteBuffer buffer = ByteBuffer.wrap("Hello".getBytes());
while (buffer.hasRemaining()) {
socketChannel.write(buffer);
}
实际应用场景中的技术选择
- 简单数据处理且注重开发便捷性:如果应用场景是简单的文件读取或网络数据传输,对性能要求不是极高,Java I/O 可能更合适。例如,开发一个简单的日志记录工具,从文件读取日志内容并写入到另一个文件或控制台,使用 Java I/O 可以快速实现功能,代码简洁易懂。
try (BufferedReader reader = new BufferedReader(new FileReader("source.log"));
BufferedWriter writer = new BufferedWriter(new FileWriter("destination.log"))) {
String line;
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.newLine();
}
} catch (IOException e) {
e.printStackTrace();
}
- 高并发、高性能场景:在高并发网络编程(如网络服务器开发)或需要处理大量数据的场景下,Java NIO 更具优势。例如开发一个高性能的网络爬虫,需要同时处理多个网络连接,NIO 的非阻塞特性和缓冲区管理可以有效提高系统的并发处理能力。
Selector selector = Selector.open();
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(8080));
serverSocketChannel.configureBlocking(false);
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
int readyChannels = selector.select();
if (readyChannels == 0) continue;
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isAcceptable()) {
// 处理新连接
} else if (key.isReadable()) {
// 处理读操作
}
keyIterator.remove();
}
}