│ │ └── example
│ │ └── coco
│ │ ├── Main.java
│ └── resources
│ └── definition.properties
│ └── test
└── target
└── classes
├── com
└── definition.properties
Main.java
public class Main {
public static void main(String[] args) {
System.out.println(Main.class.getResource("definition.properties"));
System.out.println(Main.class.getResource("/definition.properties"));
System.out.println(Main.class.getClass().getResource("definition.properties"));
System.out.println(Main.class.getClass().getResource("/definition.properties"));
System.out.println(Main.class.getClassLoader().getResource("definition.properties"));
System.out.println(Main.class.getClassLoader().getResource("/definition.properties"));
System.out.println(Main.class.getClassLoader());
System.out.println(Main.class.getClassLoader().getParent());
System.out.println(Main.class.getClassLoader().getParent().getParent());
System.out.println(Main.class.getClass().getClassLoader());
file:/D:/learning/demo/target/classes/definition.properties
file:/D:/learning/demo/target/classes/definition.properties
file:/D:/learning/demo/target/classes/definition.properties
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@4f023edb
组1 class.getResource 分析
Class.getResource源码
public java.net.URL getResource(String name) {
name = resolveName(name);
ClassLoader cl = getClassLoader0();
if (cl==null) {
return ClassLoader.getSystemResource(name);
return cl.getResource(name);
Class.resolveName源码
可以看到 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
, 其实这就是我们项目的编译后的输出目录:
组2 class.getClass().getResource分析
Main.class.getClass().getResource("definition.properties")
可以看到getResource方法的实际调用者为java.lang.Class
,而且传入进来的name
未以/
开头,所以最终name为java/lang/definition.properties
, 而这个路径是不存在的,所以
Main.class.getClass().getResource("definition.properties") 返回null
Main.class.getClass().getResource("/definition.properties")
Main.class.getClass
为 java.lang.Class
, 该类的类加载器为为null
,即ClassLoader
为BootstrapClassLoader
当为null
时最终会调用AppClassLoader.getResource
方法
ClassLoader
关于ClassLoader类加载机制,可以参考我的另一篇博客类加载机制