Java NIO(New I/O)是一種可伸縮且非阻塞的 I/O,與傳統(tǒng)的 Java IO 不同。它允許同時(shí)處理多個(gè)連接,并且能夠更快地處理 I/O 操作,是構(gòu)建高性能、可伸縮和并發(fā)應(yīng)用程序的重要組成部分。
(資料圖片)
Java NIO 的核心是 Channel、Buffer 和 Selector。Channel 是連接源和目標(biāo)的通道,Buffer 是數(shù)據(jù)傳輸?shù)木彌_區(qū),Selector 用于監(jiān)視 Channel 中的事件并選擇可用的 Channel 進(jìn)行操作。下面我們來看一下 Java NIO 的示例。
Channel
在 Java NIO 中,所有的 I/O 操作都是通過 Channel 來進(jìn)行的。Channel 類似于傳統(tǒng)的流,但又有所不同。在 Channel 中,數(shù)據(jù)可以從 Channel 中讀取到 Buffer 中,也可以將數(shù)據(jù)從 Buffer 寫入 Channel 中。
下面是一個(gè)從文件中讀取數(shù)據(jù)并輸出到控制臺(tái)的示例:
javaCopy codeimport java.io.*;import java.nio.*;import java.nio.channels.*;public class ChannelExample { public static void main(String[] args) throws IOException { RandomAccessFile file = new RandomAccessFile("test.txt", "rw"); FileChannel channel = file.getChannel(); ByteBuffer buffer = ByteBuffer.allocate(1024); while (channel.read(buffer) != -1) { buffer.flip(); while (buffer.hasRemaining()) { System.out.print((char) buffer.get()); } buffer.clear(); } channel.close(); file.close(); }}在上述示例中,我們使用 FileChannel 打開一個(gè)文件,并使用 ByteBuffer 分配一個(gè)緩沖區(qū)。然后使用 channel.read() 方法從文件中讀取數(shù)據(jù)到緩沖區(qū),使用 buffer.flip() 方法將緩沖區(qū)的指針移到緩沖區(qū)的起始位置,并使用 buffer.hasRemaining() 和 buffer.get() 方法遍歷緩沖區(qū)中的數(shù)據(jù)。
Buffer
Buffer 是一個(gè)用于讀寫數(shù)據(jù)的緩沖區(qū),它可以存儲(chǔ)任何類型的數(shù)據(jù),例如字節(jié)、字符、整數(shù)等等。Buffer 是一個(gè)抽象類,主要有以下幾種子類:ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer 和 DoubleBuffer。
下面是一個(gè)使用 ByteBuffer 存儲(chǔ)數(shù)據(jù)并將其輸出到控制臺(tái)的示例:
import java.nio.*;public class BufferExample { public static void main(String[] args) { ByteBuffer buffer = ByteBuffer.allocate(1024); buffer.put("Hello, world!".getBytes()); buffer.flip(); while (buffer.hasRemaining()) { System.out.print((char) buffer.get()); } buffer.clear(); }}在上述示例中,我們使用 ByteBuffer 分配一個(gè)緩沖區(qū),并使用 put() 方法將一個(gè)字符串存儲(chǔ)到緩沖區(qū)中。然后使用 flip() 方法將緩沖區(qū)的指針移到緩沖區(qū)的起始位置,并使用 hasRemaining() 和 get() 方法遍歷緩沖區(qū)中的數(shù)據(jù)。
Selector
Selector 用于監(jiān)視 Channel 中的事件并選擇可用的 Channel 進(jìn)行操作。在 Java NIO 中,一個(gè)線程可以同時(shí)處理多個(gè)連接,因此它比傳統(tǒng)的 Java IO 更快和更可伸縮。
下面是一個(gè)使用 Selector 監(jiān)視 Channel 的示例:
javaCopy codeimport java.io.*;import java.nio.*;import java.nio.channels.*;public class SelectorExample { public static void main(String[] args) throws IOException { ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.socket().bind(new InetSocketAddress("localhost", 8080)); serverSocketChannel.configureBlocking(false); Selector selector = Selector.open(); serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); while (true) { int readyChannels = selector.select(); if (readyChannels == 0) { continue; } for (SelectionKey key : selector.selectedKeys()) { if (key.isAcceptable()) { ServerSocketChannel server = (ServerSocketChannel) key.channel(); SocketChannel client = server.accept(); client.configureBlocking(false); client.register(selector, SelectionKey.OP_READ); } else if (key.isReadable()) { SocketChannel client = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); client.read(buffer); buffer.flip(); while (buffer.hasRemaining()) { System.out.print((char) buffer.get()); } } selector.selectedKeys().remove(key); } } }}在上述示例中,我們使用 ServerSocketChannel 打開一個(gè)服務(wù)器,并使用 Selector 監(jiān)視該服務(wù)器。當(dāng)有客戶端連接時(shí),使用 accept() 方法獲取客戶端的 SocketChannel 并注冊(cè)到 Selector 中,并指定監(jiān)聽 OP_READ 事件。當(dāng)客戶端向服務(wù)器發(fā)送數(shù)據(jù)時(shí),使用 read() 方法將數(shù)據(jù)讀取到 ByteBuffer 中,并使用 buffer.hasRemaining() 和 buffer.get() 方法遍歷 ByteBuffer 中的數(shù)據(jù)。


