在公众号回复课程,免费获取JAVA全栈课程java
做者 | 颜 群git
公众号 | 大数据和人工智能技术github
问:老师,一样的代码,为什么我将Java工程改形成Maven后,始终没法运行?
web
实现的功能:加载abc.txt文件,代码是写在了Demo类中。面试
开发工具是Idea。
微信
1.如下是普通Java工程的目录结构。
架构
Demo代码以下并发
public class Demo {
public static void main(String[] args)throws Exception{
InputStream input = new FileInputStream("src/a/abc.txt") ;
System.out.println();
}
}
此时,程序可以正常运行。app
2.如今将Java工程,改形成基于Maven的形式。目录结构以下。
jvm
Demo代码以下。
public class Demo {
public static void main(String[] args)throws Exception{
InputStream input = new FileInputStream("resources/abc.txt") ;
System.out.println();
}
}
Java工程的classpath是src;Maven工程的classpath有一个是resources。我也作了相应修改。但为什么,此时用Maven写的一样功能代码,始终报错以下。
FileNotFoundException: resources\abc.txt (系统找不到指定的路径。)
答:
(赶时间的,能够略过度析,直接看本文最后的结论和源码)
“路径问题”首先得思考一个问题:程序中使用的相对路径,是基于最终执行的字节码路径,仍是源码的路径?(好比原文件是A.java,编译后的文件是A.class。那么A在加载abc.txt时,“相对路径”是以“A.java”为参照点,仍是以“A.class”为参照点?。
上面这个问题,暂时放一放。先直接看一下形成你疑惑的根本缘由:普通的Java工程和Maven等构件工具,对.class文件和静态资源的打包路径不一样。
1.打开普通Java工程中 字节码目录,以下。
发现了什么?在普通Java工程中在编译及运行阶段,并不会处理静态资源(也就是会忽略abc.txt文件)。所以,要加载abc.txt,就得去它原来的位置,即在src源码目录下,如图所示。
因此,在使用普通Java工程时,就须要在src下面找abc.txt(由于字节码目录中的静态资源,会被忽略)。所以,加载路径是源码src中的路径,而不是字节码classes中的路径,以下。
new FileInputStream("src/a/abc.txt")
2.但Maven工程对静态资源的处理方式则不一样。
咱们知道,Maven会将字节码和静态资源 都打包在 target下,如图所示。
也就是说,maven会将Demo.class和abc.txt一同打包在 字节码(target/classes)目录下。但要注意:maven默认在读取路径时, 是基于字节码classes目录,而不是源码src目录,这与普通Java工程彻底相反。【由于maven的一个原则:就是想让 最终的执行程序,与源码相互隔离,互不干扰】。因此,若是使用了maven,静态资源的相对路径获取方式,就和普通Java工程不同了(不能再从src中读取静态资源,而要从classes中读取)。
讲到这里,有同窗可能会问“看上图,可知abc.txt就在Demo.class的上一级。所以在Demo.class中使用../abc.txt不就好了?”。看起来好像能够,但实际也不行。
使用../abc.txt的意思是,从当前路径开始,寻找上一级目录。这里说的当前路径好像就是字节码路径中的Demo.class文件自己。但实际状况真的是吗?使用如下代码,查看一下当前路径究竟是什么。
public class Demo {
public static void main(String[] args)throws Exception{//查看“当前路径” String path = new File(".").getAbsolutePath() ;
System.out.println(path);
}
}
运行结果:
D:\github\JavaCore\src\JavaBook\.
发现 ,程序在执行时当前路径是在src下,根本不是classes字节码。这又与Maven的原则相违背(Maven默认读取的路径是字节码classes目录,而不是源码src目录)。【这种特性是jvm底层决定的,咱们没法改变】。
以上,有些绕,总结就是:
java程序在使用相对路径,加载静态资源时:
1.普通Java工程加载的是源文件src目录中的静态资源;
2.Maven工程加载的是字节码目录target/classes中的静态资源;
3.在编写源码时,程序中相对路径基于的“当前路径”是在源文件src目录下 ,不是在classes目录下。(也就是当前路径是基于A.java,而不是基于A.class)。
以上三条中,“1”和“3”是一致的,因此普通Java工程方式加载静态资源,没有什么,直接写相对路径就行;但“2”和“3”在读取路径时是矛盾的,所以Maven工程在加载静态资源时,就不能使用相对路径,而须要使用绝对路径。
但还要注意,绝对路径要以动态的方式获取,防止不一样环境之间的差别。
最后,给出Maven方式,获取静态资源的代码。
public class Demo {
public static void main(String[] args)throws Exception{//动态方式,获取abc.txt的绝对路径
String path = Demo.class.getClassLoader().getResource("abc.txt").getPath();
InputStream input = new FileInputStream(path) ;
System.out.println(path);
}
}
很“简单”一个问题,是否是还挺绕人的~
- 完 -
推荐阅读
▲
本文分享自微信公众号 - 大数据和人工智能技术(Big_Data-AI)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。