Java NIO面试题

1. 什么是Java NIO?

Java NIO(New Input/Output)是从Java 1.4版本开始引入的一个新的IO API,它提供了非阻塞IO操作,使一个线程可以处理多个输入/输出通道。

2. NIO的主要组件有哪些?

NIO的主要组件包括:

  • Channels(通道):类似于流,但可以异步读写数据。
  • Buffers(缓冲区):数据容器,与通道进行交互。
  • Selectors(选择器):允许单个线程管理多个通道。

3. NIO与BIO有什么区别?

NIO是非阻塞的,支持多路复用,而BIO是阻塞的,通常一个连接需要一个线程。

// BIO
ServerSocket serverSocket = new ServerSocket(port);
Socket socket = serverSocket.accept();

// NIO
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress(port));
serverChannel.configureBlocking(false);

4. 如何在NIO中创建服务器?

使用ServerSocketChannel创建服务器,并绑定到端口。

ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress(port));

5. 如何在NIO中创建客户端?

使用SocketChannel创建客户端,并连接到服务器。

SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress(host, port));

6. NIO中的缓冲区(Buffer)有什么特点?

缓冲区是数据容器,与NIO通道进行交互,支持自动扩容。

ByteBuffer buffer = ByteBuffer.allocate(1024);

7. NIO中如何选择器(Selector)如何工作?

选择器用于监听多个通道的事件(如连接打开、数据到达),并根据事件类型进行处理。

Selector selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);

8. NIO中如何读取数据?

通过通道(Channel)读取到缓冲区(Buffer)。

while (socketChannel.isOpen()) {
    buffer.clear();
    int bytesRead = socketChannel.read(buffer);
    if (bytesRead == -1) break;
    buffer.flip();
    while (buffer.hasRemaining()) {
        // 处理数据
    }
}

9. NIO中如何写入数据?

通过通道(Channel)从缓冲区(Buffer)写入。

buffer.put("Hello".getBytes());
buffer.flip();
while (buffer.hasRemaining()) {
    socketChannel.write(buffer);
}

10. NIO中的非阻塞模式如何设置?

通过通道(Channel)的configureBlocking方法设置。

socketChannel.configureBlocking(false);

11. NIO中的通道(Channel)有哪些类型?

通道类型包括FileChannelSocketChannelServerSocketChannelDatagramChannel等。

12. NIO中的缓冲区(Buffer)有哪些类型?

缓冲区类型包括ByteBufferCharBufferShortBufferIntBufferLongBufferFloatBufferDoubleBuffer

13. NIO中的零拷贝(Zero-Copy)技术有什么优势?

零拷贝可以减少数据拷贝的开销,提高文件传输效率。

14. NIO中如何使用FileChannel进行文件操作?

FileChannel可以用于文件的读写操作。

FileChannel fileChannel = FileChannel.open(Paths.get("file.txt"), StandardOpenOption.READ);
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (fileChannel.read(buffer) != -1) {
    buffer.flip();
    // 处理数据
    buffer.clear();
}
fileChannel.close();

15. NIO中如何监听多个客户端连接?

使用选择器(Selector)监听多个客户端连接。

Selector selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
    selector.select(); // 阻塞,直到有事件就绪
    Set<SelectionKey> selectedKeys = selector.selectedKeys();
    Iterator<SelectionKey> iterator = selectedKeys.iterator();
    while (iterator.hasNext()) {
        SelectionKey key = iterator.next();
        if (key.isAcceptable()) {
            // 处理新的连接
        }
        iterator.remove();
    }
}

16. NIO中如何提高文件传输效率?

使用FileChanneltransferTotransferFrom方法进行文件传输。

FileChannel sourceChannel = FileChannel.open(Paths.get("source.txt"), StandardOpenOption.READ);
FileChannel targetChannel = FileChannel.open(Paths.get("target.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
sourceChannel.transferTo(0, sourceChannel.size(), targetChannel);
sourceChannel.close();
targetChannel.close();

17. NIO中如何实现非阻塞的服务器?

使用非阻塞模式的ServerSocketChannel和选择器(Selector)。

18. NIO中如何实现异步文件I/O?

使用AsynchronousFileChannel实现异步文件I/O。

19. NIO中如何使用内存映射文件?

使用FileChannelmap方法将文件映射到内存。

FileChannel fileChannel = FileChannel.open(Paths.get("file.txt"), StandardOpenOption.READ);
MappedByteBuffer buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size());
fileChannel.close();

20. NIO中如何使用scatter/gather读?

使用scatter读可以将数据分散到多个缓冲区中。

ByteBuffer headerBuffer = ByteBuffer.allocate(256);
ByteBuffer bodyBuffer = ByteBuffer.allocate(1024 * 1024);
ByteBuffer[] buffers = {headerBuffer, bodyBuffer};
socketChannel.read(buffers);

21. NIO中如何使用scatter/gather写?

使用gather写可以将多个缓冲区的数据写入同一个通道。

22. NIO中如何设置通道为非阻塞模式?

通过调用通道的configureBlocking方法。

channel.configureBlocking(false);

23. NIO中如何关闭通道?

通过调用通道的close方法。

channel.close();

24. NIO中如何选择器(Selector)的select方法如何工作?

select方法会阻塞,直到至少有一个注册的事件就绪。

25. NIO中如何注册通道到选择器?

通过调用通道的register方法。

channel.register(selector, SelectionKey.OP_READ);

26. NIO中如何从选择器中注销键?

通过调用选择键的cancel方法。

selectionKey.cancel();

27. NIO中如何处理通道之间的数据传输?

使用Channels类的newChannel方法和transferTo方法。

28. NIO中如何使用非阻塞模式进行网络编程?

使用非阻塞模式的SocketChannelServerSocketChannel

29. NIO中如何使用缓冲区的.flip()方法?

flip方法用于在读写模式之间切换缓冲区。

30. NIO中如何使用缓冲区的.rewind()方法?

rewind方法用于重置缓冲区的位置和标记,使其回到初始状态。