【Java核心技术/IO】35道Java IO面试题与答案
1. 什么是Java IO?
Java IO是Java提供的用于输入输出操作的API,主要处理文件、网络连接等数据的读取和写入,核心是“流”(Stream)的概念。
2. Java IO主要分为哪几类?
按数据流向分为输入流(InputStream/Reader) 和输出流(OutputStream/Writer);按处理数据类型分为字节流(处理二进制数据)和字符流(处理文本数据,需指定编码)。
3. 字节流和字符流的区别是什么?
- 处理数据:字节流以 byte (8位)为单位,处理所有二进制数据;字符流以 char (16位)为单位,仅处理文本数据。
- 编码依赖:字符流需依赖编码(如UTF-8),字节流不依赖。
- 核心类:字节流核心是 InputStream/OutputStream ,字符流是 Reader/Writer 。
4. 什么是缓冲流(Buffered Stream)?它的作用是什么?
缓冲流是包装普通流的“增强流”(如 BufferedReader 、 BufferedOutputStream ),内部维护一个缓冲区(内存数组)。
作用:减少IO与磁盘/网络的直接交互次数,提升读写效率(内存操作比硬件操作快得多)。
5. 缓冲流的 flush() 方法有什么用?
强制将缓冲区中未写满的数据立即写入目标设备(如磁盘),避免数据滞留。关闭流( close() )时会自动调用 flush() ,但需注意:若未关闭/flush,缓冲区未满的数据可能丢失。
6. 什么是节点流(Node Stream)和处理流(Processing Stream)?
- 节点流:直接连接数据源/目标的流(如 FileInputStream 直接读文件),是IO操作的“基础流”,必须存在。
- 处理流:包装节点流或其他处理流的流(如缓冲流、转换流),用于增强功能(如缓冲、编码转换),不能独立存在。
7. 转换流(InputStreamReader/OutputStreamWriter)的作用是什么?
实现字节流与字符流的转换:
- InputStreamReader :将字节流(如 FileInputStream )转为字符流,需指定编码(解决文本读取乱码问题)。
- OutputStreamWriter :将字符流转为字节流,写入时指定编码。
8. 什么是对象流(ObjectInputStream/ObjectOutputStream)?
用于对象的序列化(写对象到流)和反序列化(从流读对象) 的处理流,核心方法是 writeObject() 和 readObject() 。
注意:被序列化的类必须实现 Serializable 接口(空接口,仅作标记)
9. Serializable 接口的作用是什么?为什么是标记接口?
Serializable 是Java的序列化标记接口,作用是告诉JVM:“该类的对象允许被序列化”。
它是标记接口(无任何抽象方法),因为仅需“标记”类的序列化权限,无需强制实现特定逻辑。
10. 序列化时,如何让某个字段不被序列化?
在该字段前加 transient 关键字,JVM会忽略该字段,序列化时不写入流,反序列化时该字段会被赋默认值(如 null 、0)。
11. 什么是序列化ID(serialVersionUID)?有什么用?
serialVersionUID 是序列化类中显式声明的一个 long 类型常量(如 private static final long serialVersionUID = 1L ),用于验证序列化与反序列化的对象是否属于同一个类。
若类未显式声明,JVM会根据类结构(字段、方法)自动生成;若类结构修改(如增删字段),自动生成的ID会变,导致反序列化失败。
12. Java IO中的“流”是阻塞的还是非阻塞的?
默认的Java IO(BIO)是阻塞式的:当调用 read() / write() 时,若数据未准备好/未写完,线程会一直等待(阻塞),直到操作完成。
13. 什么是NIO?它和传统IO(BIO)的区别是什么?
NIO(New IO)是Java 1.4引入的非阻塞IO模型,核心是通道(Channel)、缓冲区(Buffer)、选择器(Selector)。
区别:
- 阻塞性:BIO是阻塞的,NIO可非阻塞(线程无需等待IO完成)。
- 数据操作:BIO以流为单位,NIO以缓冲区(批量数据)为单位。
- 线程效率:BIO一个连接需一个线程,NIO一个线程可处理多个连接(通过Selector)。
14. NIO中的Buffer和Channel是什么关系?
- Buffer:是数据的“容器”,用于存储读写的数据(NIO中数据必须通过Buffer传输)。
- Channel:是数据的“传输通道”(如文件通道、网络通道),用于连接数据源/目标。
关系:数据从Channel读入Buffer,或从Buffer写入Channel(双向,而流是单向的)。
15. NIO的Selector有什么用?
Selector(选择器)是NIO实现“单线程处理多连接”的核心组件。
它可注册多个Channel,监听Channel的IO事件(如“数据可读”“连接就绪”),线程只需通过Selector轮询“就绪事件”,再处理对应Channel,无需阻塞等待。
16. Java 7引入的NIO.2有什么新特性?
核心是 java.nio.file 包,新增特性:
- Path接口:替代传统 java.io.File ,更方便处理文件路径。
- Files工具类:提供静态方法,简化文件操作(如 Files.readAllLines() 读文件、 Files.copy() 复制文件)。
- 异步IO(Asynchronous IO):支持异步的文件和网络IO,操作完成后通过回调/Future通知。
17. File 类的作用是什么?它能操作文件内容吗?
File 类是文件/目录路径的抽象表示,用于操作文件的“元数据”(如判断文件是否存在、创建/删除文件、获取文件大小),但不能直接操作文件内容(读写内容需通过流或NIO的Channel)。
18. 如何用Java IO复制一个文件?
核心逻辑:用输入流读源文件,用输出流写目标文件,通过缓冲区提升效率。示例代码思路:
java
try (InputStream in = new BufferedInputStream(new FileInputStream(“源文件路径”));
OutputStream out = new BufferedOutputStream(new FileOutputStream(“目标文件路径”))) {
byte[] buf = new byte[1024]; // 1KB缓冲区
int len;
while ((len = in.read(buf)) != -1) { // 读入缓冲区
out.write(buf, 0, len); // 从缓冲区写出
}
} catch (IOException e) {
e.printStackTrace();
}
19. Reader 类的 read() 方法返回值是什么意思?
Reader 的 read() 有两种常用重载:
- int read() :读取一个字符,返回该字符的Unicode值(0-65535);若已到流末尾,返回 -1 。
- int read(char[] cbuf) :读取字符到数组 cbuf ,返回实际读取的字符数;若到末尾,返回 -1 。
20. 为什么推荐用“try-with-resources”语法处理IO流?
传统 try-catch-finally 需手动关闭流( close() ),若忘记关闭或 finally 中代码出错,会导致流泄漏。
try-with-resources (Java 7+)会自动关闭实现 AutoCloseable 接口的资源(所有IO流都实现了该接口),代码更简洁,且能确保资源关闭。
21. AutoCloseable 和 Closeable 接口的关系是什么?
- Closeable 是Java 5引入的接口,仅含 void close() throws IOException 方法,用于IO资源关闭。
- AutoCloseable 是Java 7引入的父接口,含 void close() throws Exception 方法,范围更广(支持所有需自动关闭的资源)。
关系: Closeable 继承自 AutoCloseable ,IO流实现 Closeable ,因此可被 try-with-resources 自动关闭。
22. 什么是字符编码?IO操作中如何避免乱码?
字符编码是“字符与二进制数据的映射规则”(如UTF-8、GBK)。乱码源于“写入编码与读取编码不一致”。
避免乱码:
- 字符流/转换流操作时,显式指定编码(如 new InputStreamReader(in, “UTF-8”) ),不依赖系统默认编码。
- 统一项目编码(如全用UTF-8)。
23. PrintStream 和 PrintWriter 的区别是什么?
两者都是“打印流”,支持格式化输出(如 print() 、 println() ),区别:
- 流类型: PrintStream 是字节流, PrintWriter 是字符流。
- 编码支持: PrintWriter 构造时可显式指定编码, PrintStream 需通过 OutputStreamWriter 间接指定。
- 异常处理:两者都不抛 IOException ,需通过 checkError() 方法检查是否有错误。
24. 什么是数据流(DataInputStream/DataOutputStream)?
数据流是处理“基本数据类型(如int、double) ”的字节流,可直接读写基本数据类型,无需手动转换为字节数组。
示例: dataOut.writeInt(100) 写入int, dataIn.readInt() 读取int。
25. 随机访问文件(RandomAccessFile)有什么特点?
RandomAccessFile 是特殊的IO类,支持“随机读写”(可跳转到文件任意位置操作),无需从头顺序读写。
核心方法:
- seek(long pos) :跳转到文件指定位置(字节偏移量)。
- readXXX() / writeXXX() :读写基本数据类型。
用途:适用于大文件分段读写(如断点续传)。
26. IO流关闭的顺序是什么?为什么?
若多个流嵌套(如缓冲流包装文件流),关闭顺序是“先关外层流,再关内层流”;但实际用 try-with-resources 时,会自动按“声明逆序”关闭,无需手动控制。
原因:外层流依赖内层流,关闭外层流时会自动关闭内层流(避免内层流泄漏);若先关内层流,外层流关闭时可能报错。
27. 什么是“流泄漏”?如何避免?
流泄漏是“IO流未关闭,导致系统资源(如文件句柄、网络连接)被占用,无法释放”,长期会导致资源耗尽。
避免方式:
- 用 try-with-resources 自动关闭流。
- 传统方式中,在 finally 块中确保 close() (需判空,避免空指针)。
28. NIO中的“零拷贝”是什么意思?有什么优势?
零拷贝(Zero-Copy)是NIO优化IO性能的技术,指“数据从内核空间直接传输到目标设备(如网络),无需在内核空间与用户空间之间拷贝”(传统IO需2-4次拷贝)。
优势:减少CPU拷贝开销,提升大文件传输效率(如文件下载、视频流),常见于 FileChannel.transferTo() 方法。
29. 什么是BIO、NIO、AIO?它们的适用场景是什么?
- BIO:阻塞IO,简单易实现,适用于“连接数少、IO操作快”的场景(如本地文件操作、简单Socket服务)。
- NIO:非阻塞IO,适用于“连接数多、IO操作慢”的场景(如高并发网络服务,如Tomcat底层)。
- AIO:异步IO(NIO.2),适用于“需异步通知、不希望阻塞线程”的场景(如高性能服务器,如Netty的AIO模式)。
30. Netty框架和Java NIO的关系是什么?
Netty是基于Java NIO的“高性能网络编程框架”,它封装了NIO的复杂细节(如Selector轮询、缓冲区管理、线程模型),提供更简洁的API和更稳定的性能,解决了NIO的“空轮询”“线程安全”等问题,是企业级网络服务(如RPC、网关)的首选。
31. ByteArrayInputStream 和 ByteArrayOutputStream 的作用是什么?
两者是“内存流”,数据读写基于字节数组(内存),不依赖磁盘/网络,速度快。
- ByteArrayInputStream :从字节数组读数据(模拟输入流)。
- ByteArrayOutputStream :向字节数组写数据(模拟输出流),可通过 toByteArray() 获取最终字节数组。
用途:适用于内存中数据转换(如字符串与字节数组互转)、测试IO逻辑。
32. 什么是字符集(Charset)?如何获取系统默认字符集?
字符集(Charset)是字符编码的集合(如UTF-8、GBK), java.nio.charset.Charset 类是其抽象表示。
获取系统默认字符集:
- Charset.defaultCharset() :返回JVM默认字符集(依赖操作系统和JVM配置)。
- System.getProperty(“file.encoding”) :通过系统属性获取,与 defaultCharset() 结果一致。
33. IO操作中, read() 方法返回-1表示什么?
read() 返回-1表示“已到达流的末尾”(没有更多数据可读取),是IO读取循环的终止条件(如 while ((len = in.read(buf)) != -1) )。
34. 为什么 FileInputStream 的 read() 方法返回int类型,而不是byte类型?
byte 类型范围是 -128~127 ,若直接返回byte,当读取的字节是 0xFF (对应十进制255)时,会被解析为 -1 ,与“流末尾”的 -1 冲突。
返回int时,会将byte的8位数据“高位补0”转为32位int(如 0xFF 转为 255 ),仅当真正到流末尾时返回 -1 ,避免歧义。
35. 总结Java IO的核心组件和设计模式?
- 核心组件:流(字节流/字符流)、缓冲区、通道、选择器、Path、Files工具类。
- 设计模式:
- 装饰器模式:处理流包装节点流(如缓冲流装饰文件流),增强功能。
- 适配器模式:转换流(如 InputStreamReader )将字节流适配为字符流。
- 工厂模式: Files.newBufferedReader() 等方法创建流实例。