目录结构
├── 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
方法