目录结构
├── src │ ├── main │ ├── java │ │ └── com │ │ └── example │ │ └── coco │ │ ├── Main.java │ └── resources │ └── definition.properties │ └── test └── target └── classes ├── com └── definition.properties 复制代码
Main.java
public class Main { public static void main(String[] args) { // 组1 System.out.println(Main.class.getResource("definition.properties")); System.out.println(Main.class.getResource("/definition.properties")); // 组2 System.out.println(Main.class.getClass().getResource("definition.properties")); System.out.println(Main.class.getClass().getResource("/definition.properties")); // 组3 System.out.println(Main.class.getClassLoader().getResource("definition.properties")); System.out.println(Main.class.getClassLoader().getResource("/definition.properties")); // AppClassLoader System.out.println(Main.class.getClassLoader()); // ExtClassLoader System.out.println(Main.class.getClassLoader().getParent()); // BootstrapClassLoader(Java中用null表示) System.out.println(Main.class.getClassLoader().getParent().getParent()); System.out.println(Main.class.getClass().getClassLoader()); } 复制代码
输出
null file:/D:/learning/demo/target/classes/definition.properties null file:/D:/learning/demo/target/classes/definition.properties file:/D:/learning/demo/target/classes/definition.properties null sun.misc.Launcher$AppClassLoader@18b4aac2 sun.misc.Launcher$ExtClassLoader@4f023edb null null 复制代码
public java.net.URL getResource(String name) { name = resolveName(name); ClassLoader cl = getClassLoader0(); if (cl==null) { // A system class. return ClassLoader.getSystemResource(name); } return cl.getResource(name); } 复制代码
resolveName
方法会去判断 传入的参数
name
是否以
/
开头,如果不是
/
开头,则会使用当前方法的调用类
com.example.coco.Main
所在包作为前缀(
com/example/coco/
),然后拼接参数name(
definition.properties
),最终返回
com/example/coco/definition.properties
,但是显然这个路径是不存在的,所以
Main.class.getResource("definition.properties") 返回 null 复制代码
而如果是 /
开头, resolveName
返回 definition.properties
,继续调用 ClassLoader.getResource
方法
/D:/learning/demo/target/classes/definition.properties
那是如何定位的呢?
Main.java
的ClassLoader是 AppClassLoader
, 它会从 classpath
中去查找 definition.properties
是否存在
classpath是什么?
当你使用Idea点运行 Main.java
的时候,其实是在拼一串命令行,类似 java Main
,只不过Idea拼接了很多参数,如下图:
拷贝出来如下:
"C:/Program Files/Java/jdk1.8.0_181/bin/java.exe" "-javaagent:C:/Program Files/JetBrains/IntelliJ IDEA 2019.3/lib/idea_rt.jar=54345:C:/Program Files/JetBrains/IntelliJ IDEA 2019.3/bin" -Dfile.encoding=UTF-8 -classpath "C:/Program Files/Java/jdk1.8.0_181/jre/lib/charsets.jar;C:/Program Files/Java/jdk1.8.0_181/jre/lib/deploy.jar; C:/Program Files/Java/jdk1.8.0_181/jre/lib/ext/access-bridge-64.jar; C:/Program Files/Java/jdk1.8.0_181/jre/lib/ext/cldrdata.jar; C:/Program Files/Java/jdk1.8.0_181/jre/lib/ext/dnsns.jar; C:/Program Files/Java/jdk1.8.0_181/jre/lib/ext/jaccess.jar; C:/Program Files/Java/jdk1.8.0_181/jre/lib/ext/jfxrt.jar; C:/Program Files/Java/jdk1.8.0_181/jre/lib/ext/localedata.jar; C:/Program Files/Java/jdk1.8.0_181/jre/lib/ext/nashorn.jar; C:/Program Files/Java/jdk1.8.0_181/jre/lib/ext/sunec.jar; C:/Program Files/Java/jdk1.8.0_181/jre/lib/ext/sunjce_provider.jar; C:/Program Files/Java/jdk1.8.0_181/jre/lib/ext/sunmscapi.jar; C:/Program Files/Java/jdk1.8.0_181/jre/lib/ext/sunpkcs11.jar; C:/Program Files/Java/jdk1.8.0_181/jre/lib/ext/zipfs.jar; C:/Program Files/Java/jdk1.8.0_181/jre/lib/javaws.jar; C:/Program Files/Java/jdk1.8.0_181/jre/lib/jce.jar; C:/Program Files/Java/jdk1.8.0_181/jre/lib/jfr.jar; C:/Program Files/Java/jdk1.8.0_181/jre/lib/jfxswt.jar; C:/Program Files/Java/jdk1.8.0_181/jre/lib/jsse.jar; C:/Program Files/Java/jdk1.8.0_181/jre/lib/management-agent.jar; C:/Program Files/Java/jdk1.8.0_181/jre/lib/plugin.jar; C:/Program Files/Java/jdk1.8.0_181/jre/lib/resources.jar; C:/Program Files/Java/jdk1.8.0_181/jre/lib/rt.jar; D:/learning/demo/target/classes" com.example.coco.Main 复制代码
其中 classpath
这个参数的最后一行为 D:/learning/demo/target/classes
, 其实这就是我们项目的编译后的输出目录:
java.lang.Class
,而且传入进来的
name
未以
/
开头,所以最终name为
java/lang/definition.properties
, 而这个路径是不存在的,所以
Main.class.getClass().getResource("definition.properties") 返回null 复制代码
Main.class.getClass
为 java.lang.Class
, 该类的类加载器为为 null
,即 ClassLoader
为 BootstrapClassLoader
null
时最终会调用
AppClassLoader.getResource
方法