class.getResource()的用法

本文介绍Java中使用Class.getResource()及Class.getResourceAsStream()方法来获取资源文件的不同方式,对比硬编码路径和相对路径的区别,并通过实例说明如何针对不同目录结构正确加载资源。
 

class.getResource()的用法

用JAVA获取文件,听似简单,但对于很多像我这样的新人来说,还是掌握颇浅,用起来感觉颇深,大常最经常用的,就是用JAVA的File类,如要取得c:/test.txt文件,就会这样用File file = newFile("c:/test.txt");这样用有什么问题,相信大家都知道,就是路径硬编码,对于JAVA精神来说,应用应该一次成型,到处可用,并且从现实应用来讲,最终生成的应用也会部署到Windows外的操作系统中,对于linux来说,在应用中用了c:/这样的字样,就是失败,所以,我们应该尽量避免使用硬编码,即直接使用绝对路径。

  在Servlet应用中,有一个getRealPath(String str)的方法,这个方法尽管也可以动态地获得文件的路径,不秘直接手写绝对路径,但这也是一个不被建议使用的方法,那么,我们有什么方法可以更好地获得文件呢?

     那就是Class.getResource()与Class.getResourceAsStream()方法,但很多人还是不太懂它的用法,因为很多人(比如不久前的我)都不知道应该传怎么样的参数给它,当然,有些人己经用得如火纯青,这些人是不需要照顾的,在此仅给不会或者还不是很熟的人解释一点点。


比如我们有以下目录
|--project
    |--src
        |--javaapplication
            |--Test.java
            |--file1.txt
        |--file2.txt
    |--build
        |--javaapplication
            |--Test.class
            |--file3.txt
        |--file4.txt

在上面的目录中,有一个src目录,这是JAVA源文件的目录,有一个build目录,这是JAVA编译后文件(.class文件等)的存放目录
那么,我们在Test类中应该如何分别获得
file1.txt file2.txt file3.txt file4.txt这四个文件呢?

首先讲file3.txt与file4.txt
file3.txt:
方法一:File file3 = new File(Test.class.getResource("file3.txt").getFile());
方法二:File file3 = new File(Test.class.getResource("/javaapplication/file3.txt").getFile());
方法三:File file3 = new File(Test.class.getClassLoader().getResource("javaapplication/file3.txt").getFile());

file4.txt:
方法一:File file4 = new File(Test.class.getResource("/file4.txt").getFile());
方法二:File file4 = new File(Test.class.getClassLoader().getResource("file4.txt").getFile());

很好,我们可以有多种方法选择,但是file1与file2文件呢?如何获得?
答案是,你只能写上它们的绝对路径,不能像file3与file4一样用class.getResource()这种方法获得,它们的获取方法如下
假如整个project目录放在c:/下,那么file1与file2的获取方法分别为
file1.txt
方法一:File file1 = new File("c:/project/src/javaapplication/file1.txt");
方法二:。。。没有

file2.txt
方法一:File file2 = new File("c:/project/src/file2.txt");
方法二:。。。也没有

总结一下,就是你想获得文件,你得从最终生成的.class文件为着手点,不要以.java文件的路径为出发点,因为真正使用的就是.class,不会拿个.java文件就使用,因为java是编译型语言嘛

至于getResouce()方法的参数,你以class为出发点,再结合相对路径的概念,就可以准确地定位资源文件了,至于它的根目录嘛,你用不同的IDEbuild出来是不同的位置下的,不过都是以顶层package作为根目录,比如在Web应用中,有一个WEB-INF的目录,WEB-INF目录里面除了web.xml文件外,还有一个classes目录,没错了,它就是你这个WEB应用的package的顶层目录,也是所有.class的根目录“/”,假如clasaes目录下面有一个file.txt文件,它的相对路径就是"/file.txt",如果相对路径不是以"/"开头,那么它就是相对于.class的路径。。

还有一个getResourceAsStream()方法,参数是与getResouce()方法是一样的,它相当于你用getResource()取得File文件后,再new InputStream(file)一样的结果

  

class.getResource("/") --> 返回class文件所在的顶级目录,一般为包名的顶级目录。 --> file:/home/duanyong/workspace/cxxx/xxxx/bin/WEB-INF/classes/
class.getResource("/xxx.txt") --> 返回顶级目录下的xxx.txt路径。 file://..../bin/WEB-INF/classes/xxx.txt

getResource(String path),path是以class文件的顶级目标所在的相对路径。如果顶级目录为classes,在classes/xxx/yyy.txt这样一个文件。取得yyy.txt的语法为:class.getResource("/xxx/yyy.txt");

示例代码:
  1. //取得classes顶级目录下的/xxx/yyy.txt文件   
  2. System.out.println(Test.class.getResource("/xxx/yyy.txt"));   
  3. //取得本class的上路径   
  4. System.out.println(Test.class.getResource(Test.class.getSimpleName() + ".class"));          



结果:
file:/home/duanyong/workspace/test/bin/WEB-INF/classes/xxx/yyy.txt
file:/home/duanyong/workspace/test/bin/WEB-INF/classes/cn/duanyong/test/Test.class
<think> 我们正在讨论如何在Java中读取JAR包内的资源文件,特别是使用`Class.getResource()`和`ClassLoader.getResource()`方法。用户之前的问题是关于路径对应,现在想深入了解这两个方法的用法、区别以及在处理JAR包文件路径时的应用。 首先,我们需要明确这两个方法的核心机制: 1. **`Class.getResource()`**: - 有两种路径写法:以`/`开头和不以`/`开头。 - 以`/`开头:表示从类路径的根目录开始查找(绝对路径)。 - 不以`/`开头:表示相对于当前类所在的包路径(相对路径)。 - 内部实现会委托给`ClassLoader`,但会对路径进行处理。 2. **`ClassLoader.getResource()`**: - 只有一种路径写法:必须以非`/`开头,因为`ClassLoader`的路径总是从类路径的根目录开始(绝对路径)。 - 不能使用相对路径(即相对于当前类的路径),因为它没有当前类的上下文。 在读取JAR包中的文件时,路径都是相对于类路径根目录的,使用正斜杠`/`分隔,并且大小写敏感。 下面我们结合具体示例来说明: ### 1. 路径基准点差异 假设我们有如下JAR结构: ``` myapp.jar ├── com/ │ └── example/ │ └── App.class └── config/ └── app.properties ``` #### 使用`Class.getResource()`: - 从`App.class`中读取`app.properties`: ```java // 绝对路径(从根目录开始) URL url1 = App.class.getResource("/config/app.properties"); // 相对路径(相对于com/example) // 错误示例:因为相对路径会查找com/example/config/app.properties,但实际不存在 URL url2 = App.class.getResource("config/app.properties"); // 返回null ``` #### 使用`ClassLoader.getResource()`: ```java // 正确:从根目录开始,不需要前导/ URL url = App.class.getClassLoader().getResource("config/app.properties"); // 错误:不能以/开头 URL errorUrl = App.class.getClassLoader().getResource("/config/app.properties"); // 返回null ``` ### 2. 方法选择建议 - **优先使用`ClassLoader.getResource()`**:因为其路径行为一致(总是从根目录开始),避免相对路径带来的混淆。 - 在需要相对当前类的路径时,才使用`Class.getResource()`(相对路径),但这种情况较少。 ### 3. JAR包读取的通用原则 无论使用哪种方法,都要注意: - **路径格式**:使用`/`分隔,无盘符和协议头。 - **资源存在性**:打包后资源是否在正确位置(可通过`jar tf myapp.jar`验证)。 ### 4. 常见问题排查 若返回`null`,检查: 1. 路径是否拼写正确(大小写、多余/缺少目录) 2. 资源是否真的打包到JAR中 3. 是否错误使用了绝对路径(在`ClassLoader`中以`/`开头) ### 5. 代码示例对比 ```java public class ResourceDemo { public static void main(String[] args) { // 使用ClassLoader(推荐) InputStream is1 = ResourceDemo.class.getClassLoader() .getResourceAsStream("config.properties"); // 使用Class的绝对路径 InputStream is2 = ResourceDemo.class .getResourceAsStream("/config.properties"); // 使用Class的相对路径(不推荐,易出错) InputStream is3 = ResourceDemo.class .getResourceAsStream("../../config.properties"); // 脆弱路径 } } ``` ### 总结表:关键区别 | 特性 | `Class.getResource()` | `ClassLoader.getResource()` | |--------------------------|----------------------------------------|---------------------------------------| | **路径基准** | 可绝对(/开头)或相对(当前类所在包) | 总是类路径根目录(不能以/开头) | | **委托机制** | 内部调用`ClassLoader`但处理路径 | 直接由类加载器实现 | | **JAR内资源读取适用性** | 高(需注意绝对/相对路径) | 高(路径行为一致) | | **空路径处理** | 空字符串返回当前类目录 | 空字符串会导致`NullPointerException` | | **路径规范化** | 自动处理`..`和`.` | 不处理相对路径符号 | > 📌 **黄金法则**: > 在JAR中读取资源时,**优先使用`ClassLoader.getResourceAsStream(String path)`**,并确保`path`是**从类路径根目录开始的相对路径**(如`"dir/file.txt"`)。 ### 相关问题 1. 为什么`getResource`返回的URL格式是`jar:file:/path.jar!/resource`? 2. 如何解决多模块项目中的资源文件冲突? 3. 在OSGi环境中读取资源文件有什么不同? 4. 如何通过`ResourceBundle`读取JAR内的国际化属性文件? 5. 使用`getResourceAsStream`读取大文件时如何避免内存溢出? 遇到具体路径问题可提供资源位置和代码片段,我会给出针对性修正方案! </think>### `Class.getResource()` 与 `ClassLoader.getResource()` 详解 在读取 JAR 包中资源文件时,这两者的路径处理机制和委托关系是核心差异点。以下是关键对比: --- #### 🧩 一、路径解析机制对比 | **特性** | `Class.getResource(String path)` | `ClassLoader.getResource(String name)` | |-----------------------|----------------------------------------------------------|---------------------------------------------------------| | **路径基准点** | 路径以 `/` 开头:类路径根目录<br>路径不以 `/` 开头:**当前类所在包目录** | **始终从类路径根目录开始**(无论是否以 `/` 开头) | | **路径规范化** | 自动解析相对路径(如 `../`) | 直接按字面路径查找,不解析相对路径 | | **空路径处理** | `getResource("")` 返回当前类所在目录 URL | `getResource("")` 返回 `null` | | **委托机制** | 内部调用 `ClassLoader.getResource()`<br>但会预处理路径 [^2] | 直接由类加载器实现查找 | --- #### 📦 二、JAR 包资源读取场景示例 假设 JAR 结构如下: ```bash myapp.jar ├── com/example/App.class └── config/settings.properties ``` ##### ✅ 正确用法: ```java // 1. 使用 ClassLoader (推荐) InputStream is1 = App.class.getClassLoader() .getResourceAsStream("config/settings.properties"); // ✅ 根目录基准 // 2. 使用 Class (绝对路径) InputStream is2 = App.class .getResourceAsStream("/config/settings.properties"); // ✅ 斜杠开头表示根目录 // 3. 使用 Class (相对路径 - 从当前类目录出发) InputStream is3 = App.class .getResourceAsStream("../../config/settings.properties"); // ✅ 但路径脆弱 ``` ##### ❌ 错误用法: ```java // 错误1: ClassLoader 尝试用绝对路径 InputStream err1 = classLoader.getResourceAsStream("/config/settings.properties"); // ❌ 返回 null // 错误2: Class 未用斜杠的相对路径 InputStream err2 = App.class.getResourceAsStream("config/settings.properties"); // ❌ 实际查找路径: com/example/config/settings.properties ``` --- #### 🔧 三、JAR 环境下的特殊行为 1. **URL 格式差异** - `Class.getResource()` 返回 URL 格式: `jar:file:/app.jar!/com/example/icon.png` - `ClassLoader.getResource()` 返回相同格式 → 两者在 JAR 中本质上调用相同底层方法 [^1] 2. **资源存在性验证** 调试技巧:打印资源 URL ```java URL url = App.class.getResource("/config/settings.properties"); System.out.println(url); // 输出: jar:file:/path/myapp.jar!/config/settings.properties ``` 3. **多 ClassLoader 场景** 在 OSGi 或容器环境中,`ClassLoader.getResource()` 可能仅访问当前模块的资源,而 `Class.getResource()` 会使用加载该类的 ClassLoader,行为更可控。 --- #### ⚠ 四、避坑指南 1. **路径分隔符** 始终用 **正斜杠 `/`**: ```java // 跨平台兼容 getResourceAsStream("data/images/logo.png") // ✅ getResourceAsStream("data\\images\\logo.png") // ❌ Windows 风格失效 ``` 2. **资源更新问题** - JAR 包内资源**不可修改**(只读) - 需动态修改的配置应放在 JAR 外部,用 `FileInputStream` 读取 [^3] 3. **性能对比** `Class.getResource()` 多一次路径预处理,但差异可忽略;优先考虑**代码可读性**。 --- #### 💡 最佳实践总结 1. **优先选择 `ClassLoader.getResourceAsStream()`** 路径行为一致,避免相对路径混淆 2. **绝对路径写法规范** ```java // ClassLoader 风格 (无前导斜杠) classLoader.getResourceAsStream("config/app.conf") // Class 风格 (必须有前导斜杠) MyClass.class.getResourceAsStream("/config/app.conf") ``` 3. **开发期验证** 使用 Maven/Gradle 标准目录结构: ```bash src/main/resources/config/app.conf → JAR 中路径: config/app.conf ``` --- ### 相关问题 1. 为什么 `getResourceAsStream` 有时返回 `null` 但文件确实存在? 2. 如何在 Spring Boot 中优先加载外部配置文件再回退到 JAR 内配置? 3. 多模块项目中如何跨模块访问资源文件? 4. 读取 JAR 中资源文件时出现乱码如何解决? 5. 如何不重启应用动态更新 JAR 内的配置文件? 遇到具体问题可提供代码片段和 JAR 结构,我会给出针对性解决方案
评论 21
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值