│ │ └── 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()); 
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) {
        // A system class.
        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.getClassjava.lang.Class, 该类的类加载器为为null,即ClassLoaderBootstrapClassLoader 当为null时最终会调用AppClassLoader.getResource方法

ClassLoader

关于ClassLoader类加载机制,可以参考我的另一篇博客类加载机制

分类:
后端
标签: