探秘 Java IO 与 NIO:春招面试知识要点

在前文中,我们深入探讨了 Java 多线程与并发相关知识,这对于提升程序性能和处理复杂业务场景至关重要。而在 Java 开发中,输入输出(IO)操作同样不可或缺,无论是读取文件、网络通信还是与数据库交互,都离不开 IO。随着技术发展,Java 又引入了 NIO(New IO),它提供了更高效的 IO 处理方式。在春招面试中,Java IO 与 NIO 也是面试官重点关注的内容,下面让我们一起深入了解。

一、Java IO

Java IO 是传统的输入输出库,它基于流(Stream)的概念,提供了一套丰富的类和接口来处理数据的输入输出。流可以分为字节流和字符流,字节流以字节为单位处理数据,字符流以字符为单位处理数据,更适合处理文本数据。

字节流

字节流主要包括InputStream和OutputStream两个抽象类,以及它们的各种实现类。例如FileInputStream用于从文件中读取字节数据,FileOutputStream用于将字节数据写入文件。

java 复制代码
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class ByteStreamExample {
    public static void main(String[] args) {
        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();
        }
    }
}

上述代码实现了从input.txt文件读取字节数据,并将其写入output.txt文件的功能。read()方法每次读取一个字节,返回值为读取到的字节数据,当读取到文件末尾时返回 - 1。write()方法将指定字节写入输出流。

字符流

字符流主要包括Reader和Writer两个抽象类及其实现类。例如FileReader用于读取文件中的字符数据,FileWriter用于将字符数据写入文件。为了提高读写效率,通常会使用BufferedReader和BufferedWriter进行缓冲处理。

java 复制代码
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class CharacterStreamExample {
    public static void main(String[] args) {
        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();
        }
    }
}

这段代码使用BufferedReader逐行读取input.txt文件中的内容,然后通过BufferedWriter将读取到的内容写入output.txt文件,readLine()方法用于读取一行字符数据,write()方法写入字符串,newLine()方法写入换行符。

面试题 1:字节流和字符流的区别是什么?

答案

  • 处理数据单位:字节流以字节(8 位)为单位处理数据,适用于处理所有类型的数据,如图片、音频、视频等二进制数据;字符流以字符为单位处理数据,一个字符根据编码方式可能占用 1 - 4 个字节,主要用于处理文本数据。
  • 处理方式:字节流直接操作字节,而字符流在操作字符时,会根据指定的字符编码进行字符与字节的转换,例如 UTF - 8、GBK 等。
  • 类继承体系:字节流继承自InputStream和OutputStream;字符流继承自Reader和Writer。

二、Java NIO

Java NIO 是 Java 1.4 引入的新 IO 库,它提供了一种基于通道(Channel)和缓冲区(Buffer)的 IO 操作方式,与传统 IO 的流方式不同。NIO 还引入了选择器(Selector),支持非阻塞 IO 操作,大大提高了 IO 效率,尤其适用于高并发场景。

通道和缓冲区

通道是一种双向的、可以进行读写操作的对象,它类似于流,但功能更强大。常见的通道有FileChannel(用于文件 IO)、SocketChannel(用于 TCP 套接字 IO)、DatagramChannel(用于 UDP 套接字 IO)等。缓冲区是一个用于存储数据的容器,所有数据都要先写入缓冲区,然后再通过通道进行传输。常用的缓冲区有ByteBuffer、CharBuffer、IntBuffer等。

java 复制代码
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class NIOExample {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("input.txt");
             FileOutputStream fos = new FileOutputStream("output.txt")) {
            FileChannel inChannel = fis.getChannel();
            FileChannel outChannel = fos.getChannel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            while (inChannel.read(buffer)!= -1) {
                buffer.flip();
                outChannel.write(buffer);
                buffer.clear();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

上述代码使用FileChannel和ByteBuffer实现了文件的复制操作。read()方法将数据从通道读取到缓冲区,flip()方法将缓冲区从写模式切换到读模式,write()方法将缓冲区中的数据写入通道,clear()方法将缓冲区清空,准备下一次写入。

选择器

选择器是 NIO 的核心组件之一,它允许一个线程管理多个通道,实现非阻塞的 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 SelectorExample {
    public static void main(String[] args) {
        try (Selector selector = Selector.open();
             ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()) {
            serverSocketChannel.bind(new InetSocketAddress(8080));
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

            while (true) {
                selector.select();
                Set<SelectionKey> selectedKeys = selector.selectedKeys();
                Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
                while (keyIterator.hasNext()) {
                    SelectionKey key = keyIterator.next();
                    if (key.isAcceptable()) {
                        ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                        SocketChannel sc = ssc.accept();
                        sc.configureBlocking(false);
                        sc.register(selector, SelectionKey.OP_READ);
                    } else if (key.isReadable()) {
                        SocketChannel sc = (SocketChannel) key.channel();
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        int bytesRead = sc.read(buffer);
                        if (bytesRead > 0) {
                            buffer.flip();
                            // 处理读取到的数据
                            buffer.clear();
                        }
                    }
                    keyIterator.remove();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

这段代码创建了一个基于选择器的服务器,监听 8080 端口。当有客户端连接时,将客户端的SocketChannel注册到选择器上,并监听读事件。当有数据可读时,读取数据并进行处理。

面试题 2:NIO 和传统 IO 的区别是什么?

答案

  • IO 模型:传统 IO 是阻塞式 IO,即当进行读写操作时,线程会一直阻塞直到操作完成;NIO 是非阻塞式 IO,线程在进行读写操作时,如果数据未准备好,不会阻塞,而是返回一个状态值,线程可以继续执行其他任务,通过选择器可以监听多个通道的状态,实现单线程管理多个 IO 操作。
  • 数据处理方式:传统 IO 基于流,数据是顺序读写的;NIO 基于通道和缓冲区,数据先写入缓冲区,再通过通道进行传输,缓冲区提供了更灵活的数据处理方式,如标记、重置等操作。
  • 适用场景:传统 IO 适用于简单的、对性能要求不高的 IO 操作;NIO 适用于高并发、对性能要求较高的场景,如网络服务器开发。

深入理解 Java IO 与 NIO,能让你在春招面试中展现扎实的技术功底。下一篇,我们将走进 Java 反射机制的世界,继续为你的面试备考添砖加瓦。

相关推荐
matlabgoodboy5 分钟前
代码编写java代做matlab程序代编Python接单c++代写web系统设计
java·python·matlab
2401_8979168415 分钟前
2018 秋招 百度二轮面试---血淋淋的经历写实
面试·职场和发展
liuyunshengsir33 分钟前
Spring Boot 使用 Micrometer 集成 Prometheus 监控 Java 应用性能
java·spring boot·prometheus
路上阡陌42 分钟前
Java学习笔记(二十四)
java·笔记·学习
何中应1 小时前
Spring Boot中选择性加载Bean的几种方式
java·spring boot·后端
苏苏大大1 小时前
zookeeper
java·分布式·zookeeper·云原生
wclass-zhengge1 小时前
03垃圾回收篇(D3_垃圾收集器的选择及相关参数)
java·jvm
涛ing1 小时前
23. C语言 文件操作详解
java·linux·c语言·开发语言·c++·vscode·vim
5xidixi2 小时前
Java TCP协议(2)
java·tcp/ip
2013crazy2 小时前
Java 基于 SpringBoot+Vue 的校园兼职平台(附源码、部署、文档)
java·vue.js·spring boot·兼职平台·校园兼职·兼职发布平台