1. 概述
本文深入探讨Java中多种文件读取方式的差异,重点对比getResourceAsStream()
方法和FileInputStream
类,并分析各自适用场景。同时介绍比FileInputStream
更优的Files.newInputStream()
方法,它在内存和性能方面更具优势。
我们将通过代码示例演示这些方法的具体使用方式,助你避开常见踩坑点。
2. 基础原理
在深入代码前,先理解getResourceAsStream()
和FileInputStream
的核心区别及典型使用场景。
2.1. 使用getResourceAsStream()
读取文件
getResourceAsStream()
方法用于从类路径(classpath)读取文件:
- 文件路径要求:必须相对于类路径
- 返回类型:
InputStream
对象 - 典型场景: ✅ 读取配置文件 ✅ 加载属性文件 ✅ 访问应用打包资源
2.2. 使用FileInputStream
读取文件
FileInputStream
类用于从文件系统读取文件:
- 文件路径要求:绝对路径或相对于当前工作目录
- 性能警告:因使用终结器(finalizer)可能导致内存和性能问题
- 推荐替代:
Files.newInputStream()
(功能相同但性能更优) - 典型场景: ✅ 读取日志文件 ✅ 处理用户数据文件 ✅ 访问外部机密文件
3. 代码示例
通过实际代码演示getResourceAsStream()
和Files.newInputStream()
的使用方法。
3.1. 使用getResourceAsStream()
创建工具类方法读取资源文件:
static String readFileFromResource(String filePath) {
try (InputStream inputStream = FileIOUtil.class.getResourceAsStream(filePath)) {
String result = null;
if (inputStream != null) {
result = new BufferedReader(new InputStreamReader(inputStream))
.lines()
.collect(Collectors.joining("\n"));
}
return result;
} catch (IOException e) {
LOG.error("Error reading file:", e);
return null;
}
}
关键点:
- 文件路径必须相对于类路径
- 使用
BufferedReader
逐行读取 - 异常处理返回
null
3.2. 使用Files.newInputStream()
创建读取文件系统文件的方法:
static String readFileFromFileSystem(String filePath) {
try (InputStream inputStream = Files.newInputStream(Paths.get(filePath))) {
return new BufferedReader(new InputStreamReader(inputStream))
.lines()
.collect(Collectors.joining("\n"));
} catch (IOException e) {
LOG.error("Error reading file:", e);
return null;
}
}
关键点:
- 文件路径需绝对路径或相对项目根目录
- 流处理方式与资源读取相同
- 避免了
FileInputStream
的性能陷阱
4. 测试验证
通过对比测试展示两种方法的路径处理差异。
4.1. 读取类路径文件
在src/main/resources
创建测试文件test.txt
:
Hello!
Welcome to the world of Java NIO.
测试代码:
@Test
void givenFileUnderResources_whenReadFileFromResource_thenSuccess() {
String result = FileIOUtil.readFileFromResource("/test.txt");
assertNotNull(result);
assertEquals(result, "Hello!\n" + "Welcome to the world of Java NIO.");
}
@Test
void givenFileUnderResources_whenReadFileFromFileSystem_thenSuccess() {
String result = FileIOUtil.readFileFromFileSystem("src/main/resources/test.txt");
assertNotNull(result);
assertEquals(result, "Hello!\n" + "Welcome to the world of Java NIO.");
}
路径对比:
| 方法 | 路径格式 | 示例路径 |
|---------------------|------------------------|------------------------------|
| readFileFromResource
| 类路径相对路径 | /test.txt
|
| readFileFromFileSystem
| 文件系统路径 | src/main/resources/test.txt
|
4.2. 读取文件系统文件
在项目外创建external.txt
文件进行测试:
@Test
void givenFileOutsideResources_whenReadFileFromFileSystem_thenSuccess() {
String result = FileIOUtil.readFileFromFileSystem("../external.txt");
assertNotNull(result);
assertEquals(result, "Hello!\n" + "Welcome to the world of Java NIO.");
}
@Test
void givenFileOutsideResources_whenReadFileFromResource_thenNull() {
String result = FileIOUtil.readFileFromResource("../external.txt");
assertNull(result);
}
关键结论:
✅ Files.newInputStream()
可读取任何文件系统路径
❌ getResourceAsStream()
仅限类路径内资源
⚠️ 类路径外文件通过getResourceAsStream()
读取将返回null
5. 总结
本文深入对比了Java中两种文件读取方式:
- **
getResourceAsStream()
**:专精类路径资源读取,适合配置文件等打包资源 - **
Files.newInputStream()
**:替代FileInputStream
的最佳选择,用于文件系统文件访问
选择建议:
- 优先使用
Files.newInputStream()
处理文件系统文件 - 配置/资源文件首选
getResourceAsStream()
- 避免在性能敏感场景使用
FileInputStream
示例代码已托管在GitHub仓库,欢迎参考实践。