文件与网络 I/O 及零拷贝

共 21 题
📑 题目列表 21 题
#
★★★

1. Files.copy 与 Files.move 的原子性

Files.copy 与 Files.move 的原子性如何保证?各自有哪些注意事项?

  • Files.copy 与 Files.move 的原子性
  • StandardCopyOption 的语义
  • 跨文件系统与同文件系统的差异

Files.copy 与 Files.move 的原子性取决于底层文件系统与操作。Files.move 在同一文件系统内通常基于 rename() 系统调用实现,是原子的(瞬间完成,不会出现中间状态);若指定 ATOMIC_MOVE 选项,则强制要求原子移动,若底层不支持则抛 AtomicMoveNotSupportedException。跨文件系统时,move 退化为复制+删除,不是原子的。Files.copy 默认不是原子的,它可能分多步复制(读一部分写一部分),复制过程中若中断会留下不完整文件;可配合 REPLACE_EXISTING 覆盖。注意事项:指定 ATOMIC_MOVE 时不能同时指定其他不完全兼容的选项;原子性依赖底层文件系统的支持(如 ext4 的 rename 原子,某些网络文件系统可能不同)。理解 copy 的非原子与 move 的同文件系统原子性是本题关键。

核心是 move 在同文件系统基于 rename 原子、跨文件系统复制+删除非原子;copy 非原子。理解原子性依赖与 ATOMIC_MOVE 选项是本题关键。

#
★★★

2. FileLock 文件锁,tryLock 非阻塞与重叠检测、跨进程互斥与 JVM 内锁的差异,进程崩溃后锁的释放

FileLock 文件锁的 tryLock 非阻塞与重叠检测、跨进程互斥与 JVM 内锁的差异、进程崩溃后锁的释放各自如何?

  • tryLock 的非阻塞语义
  • 重叠锁检测与跨进程互斥
  • 进程崩溃后锁的释放

FileLock 通过 tryLock() 非阻塞尝试获取锁,立即返回,若无法获得返回 null;lock() 则阻塞等待。重叠检测:对同一文件同一区域,若已有排他锁或冲突的共享锁,tryLock 会失败/返回 null,检测到锁重叠。文件锁是跨进程(JVM 级)的互斥机制,用于协调多个进程访问同一文件,与 JVM 内的锁(synchronized、ReentrantLock)不同:JVM 内锁只协调同一 JVM 内的线程,文件锁协调多进程。文件锁依赖操作系统:Linux 上基于建议锁(advisory),其他进程遵守才有效;进程崩溃或退出时,操作系统会自动释放其持有的文件锁,因此进程崩溃后锁会释放,不会永久占用。但若进程被 kill -9,操作系统也会清理 fd 并释放锁。理解 tryLock 非阻塞、文件锁跨进程互斥与 JVM 内锁差异、崩溃自动释放是本题关键。

核心是 tryLock 非阻塞、文件锁跨进程互斥(区别于 JVM 内锁)、崩溃时操作系统自动释放锁。理解这些语义是本题关键。

#
★★

3. FileSystems 与 in-memory 文件系统(Jimfs)

FileSystems 与 in-memory 文件系统(如 Jimfs)是什么?如何创建和使用?

  • FileSystems.getDefault 与自定义文件系统
  • Jimfs 内存文件系统
  • 使用场景

FileSystems.getDefault() 获取默认文件系统(操作系统的文件系统),FileSystems.newFileSystem() 可创建自定义文件系统(如 zip、内存文件系统)。Jimfs 是一个开源的 in-memory 文件系统,通过在内存中实现文件系统,提供与真实文件系统一致的 Path/Files API,但数据存储在内存中,不落盘。使用方式:Jimfs.newFileSystem() 创建内存文件系统,得到 FileSystem,从中获取 Path 进行 Files 操作。应用场景:测试中避免真实文件系统依赖、快速创建临时文件环境、模拟文件系统行为。好处是速度快、隔离、无需清理临时文件。理解 FileSystems 创建自定义文件系统与 Jimfs 内存文件系统的使用是本题关键。

核心是 FileSystems 可创建不同文件系统,Jimfs 提供内存文件系统用于测试与隔离。理解创建与使用场景是本题关键。

FileSystem fs = Jimfs.newFileSystem(Configuration.unix());
Path p = fs.getPath("/tmp/test.txt");
Files.writeString(p, "hello");
#
★★

4. FileSystems 与自定义文件系统

FileSystems 如何创建自定义文件系统?自定义文件系统有什么应用?

  • FileSystems.newFileSystem
  • FileSystemProvider 机制
  • 自定义文件系统的应用

FileSystems.newFileSystem(URI, Map) 可以根据 URI 创建自定义文件系统,例如 zip/jar 文件系统(jar:file:...)、内存文件系统。自定义文件系统通过 FileSystemProvider 机制实现:JDK 提供 FileSystemProvider 抽象,开发者可实现自己的 Provider 并通过 ServiceLoader 注册,然后在 FileSystems 中通过 scheme 找到对应 Provider 创建文件系统。典型应用:将 zip/jar 挂载为文件系统(ZipFileSystem)、实现内存文件系统、实现远程文件系统(如 HDFS、FTP)。自定义文件系统让 Path/Files API 统一操作不同存储后端。理解 FileSystems.newFileSystem 与 FileSystemProvider 机制(SPI 扩展)是本题关键。

核心是 FileSystems.newFileSystem 通过 FileSystemProvider SPI 创建自定义文件系统(zip、内存、远程)。理解 SPI 机制与统一 API 是本题关键。

#
★★

5. Files.createTempFile 的临时文件清理

Files.createTempFile 创建的临时文件如何清理?有哪些注意事项?

  • createTempFile 的创建
  • 临时文件清理机制
  • 避免残留

Files.createTempFile 在系统临时目录(或指定目录)创建临时文件,返回 Path。临时文件不会自动删除,需要开发者主动清理,否则会残留。清理方式:使用完调用 Files.deleteIfExists(path) 删除;或在 try-with-resources 中配合 deleteOnExit(但 JVM 退出才删,长期运行会累积);或使用框架(如 Spring 的临时文件管理)自动清理。注意事项:临时文件默认前缀为随机、后缀可指定;createTempFile 创建的临时文件权限可能受 umask 影响;长时间运行未清理会累积磁盘占用。最佳实践是使用完立即删除(finally 中 deleteIfExists),或结合自动清理机制。理解 createTempFile 不自动删除、需主动清理是本题关键。

核心是 createTempFile 不自动清理,需主动 deleteIfExists 或利用 deleteOnExit,避免残留。理解清理机制是本题关键。

#
★★

6. Files.deleteIfExists 与删除语义

Files.deleteIfExists 与 Files.delete 的删除语义有何区别?有哪些注意事项?

  • deleteIfExists 与 delete 的差异
  • 删除的异常语义
  • 空目录与非空目录

Files.delete(path) 删除文件,若文件不存在则抛 NoSuchFileException;Files.deleteIfExists(path) 删除文件,若文件不存在则直接返回 false(不抛异常)。两者都只能删除空目录(非空目录删除会抛 DirectoryNotEmptyException)。delete 与 deleteIfExists 的语义:delete 要求文件必须存在,否则异常;deleteIfExists 容忍文件不存在。删除注意事项:不能删除非空目录(需先递归删除子项);删除符号链接时删除的是链接本身而非目标;权限不足会抛 AccessDeniedException。理解 delete 与 deleteIfExists 的差异(存在性处理)与删除语义是本题关键。

核心是 delete 不存在抛异常、deleteIfExists 不存在返回 false;都只能删空目录。理解差异与删除语义是本题关键。

#
★★

7. Files.getPosixFilePermissions 与权限位

Files.getPosixFilePermissions 如何获取文件权限?POSIX 权限位是什么?

  • getPosixFilePermissions 方法
  • POSIX 权限位(rwx)
  • 权限管理与修改

Files.getPosixFilePermissions(Path) 返回文件的 POSIX 权限,类型为 Set,其中 PosixFilePermission 枚举包含 OWNER_READ、OWNER_WRITE、OWNER_EXECUTE、GROUP_READ、GROUP_WRITE、GROUP_EXECUTE、OTHERS_READ、OTHERS_WRITE、OTHERS_EXECUTE 共 9 个权限位,对应 Unix 的 rwx 三组(所有者、组、其他)。通过 Files.setPosixFilePermissions(Path, Set) 可修改权限。POSIX 权限位是 Unix 文件系统的经典权限模型(rwx 三组九位)。注意:getPosixFilePermissions 只在支持 POSIX 权限的文件系统(如 Unix)上可用,Windows 不支持会抛 UnsupportedOperationException。理解 POSIX 权限位(rwx 三组)与 get/set 方法及平台限制是本题关键。

核心是 POSIX 权限位 9 位(rwx 三组),getPosixFilePermissions 获取、set 修改,仅 Unix 支持。理解权限模型与平台限制是本题关键。

#
★★

8. Files.isSameFile 与符号链接

Files.isSameFile 如何判断两个路径是否指向同一文件?与符号链接的关系是什么?

  • isSameFile 的语义
  • 符号链接解析
  • 与 equals 的差异

Files.isSameFile(path1, path2) 判断两个路径是否指向同一个文件(通过比较文件标识,如 device + inode),即使路径不同(如一个是符号链接、一个是相对路径、一个是绝对路径)也能判断。它比 Path.equals 更准确,因为 equals 只比较路径字符串,而 isSameFile 比较实际文件。与符号链接的关系:isSameFile 会解析符号链接,若两个路径经符号链接解析后指向同一文件,则返回 true。符号链接本身与目标文件是不同文件,isSameFile 对链接与目标会返回 false(除非链接指向目标)。注意:isSameFile 需要访问文件系统,若文件不存在行为由实现决定。理解 isSameFile 比较实际文件(解析符号链接)而非路径字符串是本题关键。

核心是 isSameFile 比较实际文件(device+inode),解析符号链接,比 equals 更准确。理解其与符号链接的关系是本题关键。

#
★★

9. Files.readAllBytes vs Files.lines 的内存代价

Files.readAllBytes 与 Files.lines 的内存代价有何差异?各自适用什么场景?

  • readAllBytes 一次性读入内存
  • lines 流式处理
  • 内存代价与适用场景

Files.readAllBytes(path) 将整个文件一次性读入内存,返回 byte[],内存代价等于文件大小,适合小文件;大文件会占用大量内存,甚至 OOM。Files.lines(path) 返回 Stream,按行流式读取,内部使用 BufferedReader 逐行读取,内存代价低(只缓存当前行),适合大文件逐行处理。readAllBytes 适合小文件、需要整体字节数据;lines 适合大文件、逐行处理(如日志分析)。注意 lines 返回的 Stream 使用后需关闭(try-with-resources)释放底层文件句柄。理解两者内存代价差异(一次性读入 vs 流式逐行)与适用场景是本题关键。

核心是 readAllBytes 一次性读入内存(适合小文件),lines 流式逐行(适合大文件)。理解内存代价与场景选择是本题关键。

#
★★

10. Files.readAttributes 与文件元数据

Files.readAttributes 如何读取文件元数据?有哪些属性视图?

  • readAttributes 方法
  • 文件属性视图(Basic/Dos/Posix)
  • 元数据读取

Files.readAttributes(path, attributesClass) 读取文件的元数据,返回属性对象。常见属性视图:BasicFileAttributes(基本属性:size、lastModified、isDirectory、isSymbolicLink、文件时间等)、DosFileAttributes(DOS 属性:hidden、readonly、archive)、PosixFileAttributes(POSIX 属性:owner、group、permissions)。也可用 Files.readAttributes(path, String) 按名称读取单个属性(如 "size"、"lastModifiedTime")。文件元数据用于判断文件类型、大小、时间、权限等。注意 readAttributes 支持跟随或不跟随符号链接(LinkOption.NOFOLLOW_LINKS)。理解 readAttributes 与各类属性视图、元数据读取是本题关键。

核心是 readAttributes 读取元数据,属性视图有 Basic/Dos/Posix,支持按名读取。理解属性视图与元数据是本题关键。

#
★★

11. Files.walk/Files.find 的 Stream API 集成在大目录遍历中的注意事项

Files.walk/Files.find 的 Stream API 集成在大目录遍历中有哪些注意事项?

  • walk/find 返回 Stream
  • 大目录遍历的资源与性能
  • 关闭与异常处理

Files.walk(path) 返回深度优先遍历的 Stream ,Files.find(path, depth, matcher) 返回匹配条件的 Stream。两者都返回 Stream,需注意:返回的 Stream 使用后必须关闭(try-with-resources),否则底层文件句柄/目录流不释放,可能 fd 泄漏;大目录遍历时,Stream 是惰性的,遍历过程中会打开目录流,若未关闭会泄漏资源;遍历深度(maxDepth)可限制,避免遍历过深;遍历过程中可能遇到权限异常或符号链接循环,需处理;大目录遍历性能受限于文件系统 I/O,通常用并行流或限制范围优化。此外,walk 默认不跟随符号链接(避免循环),可通过 options 控制。理解 walk/find 的 Stream 关闭、惰性遍历、深度限制与异常处理是本题关键。

核心是 walk/find 返回 Stream 需关闭(避免 fd 泄漏)、惰性遍历、限制深度、异常与符号链接处理。理解这些注意事项是本题关键。

#
★★

12. Files.walk/Files.list 的流式遍历

Files.walk 与 Files.list 的流式遍历有何区别?各自如何使用?

  • walk 的深度遍历
  • list 的单层遍历
  • 流式处理与关闭

Files.list(path) 返回目录下直接子项的 Stream (不递归),只列出一层;Files.walk(path) 返回递归遍历的 Stream(可指定深度),深度优先遍历整棵目录树。两者都返回 Stream,需关闭(try-with-resources)释放目录句柄。使用场景:list 用于列出单层目录/文件;walk 用于递归遍历整个目录树(如统计文件、批量处理)。walk 可指定 maxDepth 限制深度,配合 filter 处理。注意两者都是惰性流,遍历时打开目录流,需及时关闭。理解 walk(递归)与 list(单层)的差异及流式遍历的关闭是本题关键。

核心是 walk 递归深度遍历、list 单层列出,都返回须关闭的 Stream。理解差异与使用是本题关键。

#
★★

13. Files.walkFileTree 的递归控制

Files.walkFileTree 如何控制递归遍历?它与 Files.walk 有何区别?

  • walkFileTree 与 FileVisitor
  • 递归深度控制
  • 与 walk 的区别

Files.walkFileTree(path, FileVisitor) 使用 FileVisitor 访问器进行递归遍历,FileVisitor 提供 preVisitDirectory、visitFile、visitFileFailed、postVisitDirectory 四个回调方法,可精细控制遍历行为(如跳过目录、处理错误、决定是否继续)。相比之下,Files.walk 返回 Stream,通过 maxDepth 限制深度,通过 filter 过滤,编程模型更声明式。walkFileTree 的优势是精细控制:可以按目录跳过(返回 SKIP_SUBTREE)、处理访问失败(visitFileFailed)、收集目录信息。walk 的优势是简洁的流式处理。递归控制:walkFileTree 通过 preVisitDirectory 返回 CONTINUE/SKIP_SUBTREE/SKIP_SIBLINGS/TERMINATE 控制递归;walk 通过 maxDepth 控制深度。理解 walkFileTree 的 FileVisitor 回调与递归控制、与 walk 的区别是本题关键。

核心是 walkFileTree 用 FileVisitor 回调精细控制递归(跳过目录、处理失败),walk 用 Stream+maxDepth 声明式。理解控制方式的差异是本题关键。

#
★★

14. Files/Paths 工具类的常用操作

Files 与 Paths 工具类的常用操作有哪些?

  • Paths.get 创建路径
  • Files 的读写/判断/复制等操作
  • 常用方法汇总

Paths.get(String) 创建 Path,是常用便捷方法。Files 提供大量静态文件操作:创建(createFile、createDirectory、createTempFile)、判断(exists、isDirectory、isRegularFile、isReadable)、读写(readAllBytes、readString、write、newBufferedReader、newBufferedWriter)、复制移动(copy、move)、删除(delete、deleteIfExists)、属性(getAttribute、readAttributes、size、getLastModifiedTime)、遍历(list、walk、walkFileTree、find)、符号链接(createSymbolicLink、isSymbolicLink、readSymbolicLink)、权限(getPosixFilePermissions、setPosixFilePermissions)。Paths 与 Files 配合:Paths.get 得到 Path,Files 操作 Path。理解 Files/Paths 常用操作(创建、判断、读写、复制、删除、属性、遍历)是本题关键。

核心是 Paths.get 创建路径,Files 提供创建/判断/读写/复制/删除/属性/遍历等操作。理解常用方法覆盖是本题关键。

#
★★

15. FileChannel.force 与持久化,write 只进操作系统缓存、force 才落盘,与流式输出 flush 的语义差异

FileChannel.force 与持久化的关系是什么?write 只进缓存、force 才落盘,与流式输出 flush 有何语义差异?

  • write 只进页缓存
  • force 强制落盘
  • 与 flush 的差异

FileChannel.write() 将数据写入操作系统页缓存,并不立即落盘;force() 相当于 fsync,将页缓存中的脏数据强制刷到磁盘,确保持久化。若系统崩溃或断电,未刷盘的数据会丢失。因此需要持久化的场景(数据库、日志)写入后调用 force()。流式输出(OutputStream)的 flush() 与 force() 有本质差异:flush() 只是把 JVM 缓冲区的数据排出到操作系统(即将数据推给内核),并不保证落盘;force() 才保证数据真正写入磁盘。也就是说,flush 是"用户态缓冲 -> 内核缓冲",force/fsync 是"内核缓冲 -> 磁盘"。因此流式输出 flush 后数据仍在内核缓存,崩溃可能丢失,需配合 fsync 才算持久化。理解 write 只进缓存、force 落盘、flush 只到内核的差异是本题关键。

核心是 write 进页缓存、force(fsync) 落盘、flush 只排到内核。三者层级不同,持久化需 force/fsync。理解层次差异是本题关键。

#
★★

16. AsynchronousFileChannel 异步读写,CompletionHandler 与 Future 两种接口、线程池关联及适用场景

AsynchronousFileChannel 的异步读写如何使用?CompletionHandler 与 Future 两种接口有何区别?线程池关联与适用场景如何?

  • AsynchronousFileChannel 的异步读写
  • CompletionHandler 与 Future 接口
  • 线程池关联与适用场景

AsynchronousFileChannel 提供文件的异步读写,支持两种结果方式:传入 CompletionHandler 回调(completed/failed),或传入 null 返回 Future。异步是指读写操作立即返回,由内部线程池在 I/O 完成时执行回调或设置 Future 结果。AsynchronousFileChannel 需要关联一个线程池(AsynchronousChannelGroup),用于执行完成回调;也可使用默认线程池(系统默认)。对比:CompletionHandler 方式非阻塞、事件驱动,适合高并发异步;Future 方式可阻塞等待结果,适合需要同步获取的场景。适用场景:大文件异步读写、不阻塞主线程的 I/O、需要与回调/异步框架整合。注意:异步文件读写可指定 position(文件位置),支持并发读写。理解 CompletionHandler 与 Future 两种接口、线程池关联与适用场景是本题关键。

核心是 AsynchronousFileChannel 通过 CompletionHandler 回调或 Future 获取异步结果,需关联线程池。理解两种接口与线程池、适用场景是本题关键。

#
★★

17. RandomAccessFile 与 FileChannel 的随机访问,position 定位、读改写场景的取舍与并发差异

RandomAccessFile 与 FileChannel 的随机访问有何差异?position 定位、读改写场景的取舍与并发差异如何?

  • RandomAccessFile 与 FileChannel 的随机访问
  • position 定位
  • 读改写与并发差异

RandomAccessFile 与 FileChannel 都支持随机访问(通过 position 定位到任意位置读写)。RandomAccessFile 提供 seek() 定位、read/write 方法,是传统随机访问方式;FileChannel 通过 position(long) 定位,配合 read/write 或 transferTo。取舍:RandomAccessFile 简单直接,适合小文件随机访问;FileChannel 更现代化,支持内存映射(map)、零拷贝(transferTo)、配合 Selector 等,适合高性能场景。读改写(read-modify-write)场景:两者都需先定位读取,修改后写回。并发差异:RandomAccessFile 的实例不是线程安全的,多线程访问同一实例需外部同步;FileChannel 的 position 会随读写改变,多线程并发读写需注意 position 竞争,可通过局部 position 或按块并发。理解两者的随机访问、position 定位、读改写与并发差异是本题关键。

核心是两者都支持随机访问,FileChannel 更强大(mmap/transferTo),两者 position 处理与并发安全有差异。理解取舍与并发是本题关键。

#

18. WatchService(JDK 7+)在文件系统变更监控中的事件类型与注册机制

WatchService 在文件系统变更监控中的事件类型与注册机制是什么?

  • WatchService 的事件类型
  • 注册机制(Path.register)
  • 文件监控应用

WatchService(JDK 7+)用于监控文件系统变更。事件类型包括:ENTRY_CREATE(文件/目录创建)、ENTRY_DELETE(删除)、ENTRY_MODIFY(修改)、OVERFLOW(事件丢失,因缓冲溢出)。注册机制:通过 Path.register(watchService, events) 注册目录(而非单个文件),WatchService 监控该目录下的变更;通过 watchService.take() 阻塞获取 WatchKey,watchKey.pollEvents() 获取变更事件。应用场景:配置热更新、日志文件监控、目录自动化处理。注意:WatchService 监控的是目录,事件为目录下的直接变更;OVERFLOW 表示事件可能丢失需处理。理解 WatchService 的事件类型(create/delete/modify/overflow)与注册机制(注册目录)是本题关键。

核心是 WatchService 监控目录,事件有 create/delete/modify/overflow,通过 register 注册、take 获取。理解事件类型与注册机制是本题关键。

#

19. sendfile/splice 系统调用与 Java

sendfile/splice 系统调用与 Java 的关系是什么?Java 如何利用它们?

  • sendfile/splice 系统调用
  • Java 的零拷贝实现
  • 应用场景

sendfile 系统调用用于在文件与 socket 之间直接传输数据,由内核完成,无需经过用户态,实现零拷贝;splice 系统调用用于在两个文件描述符之间移动数据,同样在内核中完成,也用于零拷贝。Java 通过 FileChannel.transferTo()/transferFrom() 在 Linux 上调用 sendfile,实现文件到 socket/文件通道的零拷贝传输;Netty 的 FileRegion、Kafka 的日志发送都利用此特性。splice 在 Java 标准库中没有直接暴露,但可通过 JNI 或框架(如 Netty 原生)使用。Java 利用 sendfile 的场景:高吞吐文件传输、日志/消息队列发送。理解 sendfile/splice 是内核零拷贝系统调用,Java 通过 transferTo 利用 sendfile 是本题关键。

核心是 sendfile/splice 是内核零拷贝系统调用,Java 通过 transferTo 利用 sendfile,splice 无直接 Java API。理解其关系是本题关键。

#

20. JDK 25 中 Path 的符号链接(Symbolic Link)解析与 LinkOption.NOFOLLOW_LINKS 的安全语义

JDK 25 中 Path 的符号链接解析与 LinkOption.NOFOLLOW_LINKS 的安全语义是什么?

  • 符号链接解析
  • NOFOLLOW_LINKS 选项
  • 安全语义

Path 操作默认会跟随符号链接(例如 Files.size、readAttributes 若未指定 NOFOLLOW_LINKS 会解析链接到目标)。LinkOption.NOFOLLOW_LINKS 指示不跟随符号链接,对链接本身操作(如获取链接属性、删除链接而非目标)。安全语义:在安全敏感场景(如权限检查、文件遍历),应使用 NOFOLLOW_LINKS 避免符号链接攻击(symlink attack),防止恶意链接指向敏感文件导致越权访问。例如,遍历目录时不应跟随符号链接跳出目录范围。JDK 25 中符号链接处理与安全语义保持一致:默认跟随,可用 NOFOLLOW_LINKS 禁止,避免安全风险。理解 NOFOLLOW_LINKS 的语义(不跟随链接)与安全意义(防止 symlink attack)是本题关键。

核心是默认跟随符号链接、NOFOLLOW_LINKS 不跟随并操作链接本身,安全场景使用以防 symlink attack。理解安全语义是本题关键。

#

21. ZipFileSystem,通过 FileSystems.newFileSystem 把 zip/jar 挂载为文件系统,与解压读写的应用场景

ZipFileSystem 如何通过 FileSystems.newFileSystem 把 zip/jar 挂载为文件系统?与解压读写有何应用场景?

  • ZipFileSystem 的挂载
  • FileSystems.newFileSystem 的 zip 支持
  • 应用场景

ZipFileSystem 允许通过 FileSystems.newFileSystem(URI.create("jar:file:..."), map) 将 zip/jar 文件挂载为一个文件系统,之后可用统一的 Path/Files API 读取 zip 内的内容(如列出、读取、创建 zip 内文件),无需显式解压。这是 JDK 内置的 zip 文件系统提供者。应用场景:读取 jar 内资源、检查 zip 内容、在 zip 内进行文件操作而不解压到磁盘、避免临时文件。与解压读写的区别:解压是手动把 zip 内容写出到磁盘再读取,会占用磁盘且需清理;ZipFileSystem 直接在内存/zip 中操作,更高效、隔离。理解 ZipFileSystem 通过 newFileSystem 挂载 zip 及与解压读写的应用差异是本题关键。

核心是 ZipFileSystem 把 zip/jar 挂载为文件系统,用统一 API 操作,避免显式解压的磁盘与清理开销。理解挂载与场景是本题关键。

try (FileSystem fs = FileSystems.newFileSystem(URI.create("jar:file:/tmp/a.zip"), Map.of())) {
    Path p = fs.getPath("/entry.txt");
    String s = Files.readString(p);
}