一文终结 Java 路径加载争议:斜杠 什么时候该加,什么时候不该加

01 引言

在日常开发测试中,我们经常需要读取文件,但是文件的相对路径总是搞不清楚。要么丢了/,要么多了/,要么缺少src/main,要门测试好好的,发布到服务器上总是找不到文件…

本节将彻底搞明白这个问题,以免在使用过程中重复试错。

02 核心概念

Java中相对路径的基准是什么?

Java 中使用相对路径(如 new File("config/app.properties"))时,路径的基准不是项目根目录,不是classpath,而是 JVM 启动时所在的操作系统工作目录,即系统属性 user.dir

// 打印当前工作目录,这就是相对路径的基准
System.out.println(System.getProperty("user.dir"));

03 IDE 中的相对路径

IntelliJ IDEAEclipseIDE 中运行项目时,IDE 默认将工作目录设置为项目根目录(即 pom.xmlbuild.gradle 所在目录)

示例项目结构:

my-project/
├── src/
│   ├── main/
│   │   ├── java/
│   │   ├── resources/
│   │   └── image/          ← 非标准位置
│   │       ├── cropped.jpg
│   │       └── ygyz.png
│   └── test/
├── pom.xml
└── target/

示例项目结构中,image是自定义的包,属于非标准位置。在项目发布中需要特殊处理,否则无法读取文件。

IDE 中,以下代码可以工作:

// IDE 中 user.dir = 项目根目录,所以能找到
File imageFile = new File("src/main/image/cropped.jpg");

⚠️ 但这只是巧合 — 因为 IDE 恰好把 user.dir 设成了项目根目录。打包成 jar 后,这段代码就会失效。

如上面的目录结构: image 文件夹位于 src/main/image/,与 javaresources 同级。如何处理呢?会有不同的方案。

3.1 迁移到标准位置

标准位置位于src/main/resources/static下:

src/main/resources/
├── static/
│   └── image/          ← 新位置
│       ├── cropped.jpg
│       └── ygyz.png
├── i18n/
├── templates/
└── application.yml

迁移后:

  • 代码中通过 classpath 读取
  • 前端可直接访问:http://localhost:8080/image/cropped.jpg
  • 打包后文件在 jar 内,部署无问题

3.2 Maven 配置

Maven配置可以保留现有的结构。

<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
        </resource>
        <resource>
            <directory>src/main/image</directory>
            <targetPath>static/image</targetPath>
        </resource>
    </resources>
</build>

在编译的时候,就会将文件自动打包到src/main/resources/static/image下,使用方式就可以和3.1一致了。

但是使用者会有一个疑问,明明看到src/main/resources/static/下面没有数据,只能通过target查看。

04 文件读取方法

无论是采用上面的3.13.2都是将文件统一到了一处:src/main/resources/static/image/

4.1 文件、路径读取

File file = new File("src/main/resources/static/image/ygyz.jpg");
System.out.println(file.exists());
// true

Path path = Paths.get("src/main/resources/static/image/ygyz.jpg");
System.out.println(path.toFile().exists());
// true

注意:这里的前面已经不能加/,否则无法读取文件

4.2 ClassPathResource读取

这个是Spring官方的读取方式。

ClassPathResource classPathResource = new ClassPathResource("static/image/ygyz.jpg");
System.out.println(classPathResource.exists());

这里默认从classpath下加载,前缀是否加/都不影响,标准模式是不加。

4.3 getResourceAsStream

// 方式一:以 / 开头,从 classpath 根开始
InputStream is = getClass().getResourceAsStream("/static/image/ygyz.jpg");

// 方式二:通过类加载器,不需要 /
InputStream is = getClass().getClassLoader()
    .getResourceAsStream("static/image/ygyz.jpg");

4.4 Spring @Value 注入

@Service
public class ImageService {

    @Value("classpath:static/image/cropped.jpg")
    private Resource imageResource;

    public byte[] readImage() throws IOException {
        return FileCopyUtils.copyToByteArray(imageResource.getInputStream());
    }
}

4.5 ResourceUtils

org.springframework.util.ResourceUtils

File file1 = ResourceUtils.getFile("classpath:static/image/ygyz.jpg");
System.out.println(file1.exists());
// true

File file2 = ResourceUtils.getFile("src/main/resources/static/image/ygyz.jpg");
System.out.println(file2.exists());
// true

05 核心问题:/ 到底加不加?

“类要加斜杠,加载器不加”

  • 类的 getResource() / getResourceAsStream() 必须加
  • 类加载器的 getResource() / getResourceAsStream() 不用加

区别

Class.getResource() 的设计逻辑:

  • / 开头 → 从 classpath 根目录 开始查找
  • 不以 / 开头 → 从 当前类所在的包目录 开始查找
// 假设当前类在 com.example.service 包下

// ✅ 从 classpath 根开始找
getClass().getResourceAsStream("/static/image/cropped.jpg");

// ❌ 从 com/example/service/ 包下开始找(找不到!)
getClass().getResourceAsStream("static/image/cropped.jpg");

ClassLoader.getResource() 的设计逻辑:

  • 永远从 classpath 根目录 开始查找
  • / 开头反而会被当作路径的一部分,导致找不到
// ✅ 正确:从 classpath 根开始找
getClass().getClassLoader().getResourceAsStream("static/image/cropped.jpg");

// ❌ 错误:把 /static 当作目录名的一部分
getClass().getClassLoader().getResourceAsStream("/static/image/cropped.jpg");

06 小结

Java 相对路径以 user.dir 为基准,开发和部署环境可能不同。生产环境优先使用 classpath 加载资源,避免依赖文件系统相对路径。记住口诀:类要加斜杠,加载器不加。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

智_永无止境

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值