java的getResource方法

getResource是Class类中的一个方法
作用呢就是配置文件的读取

首先我们得知道,在new一个对象的时候会在java堆区中生成一个代表这个类的java.lang.Class对象,(不能主动创建,只能获取)作为这个类的各种数据的访问入口
我们怎样使用对应的Class对象呢?
三种方法 ,先看看我的目录结构
在这里插入图片描述

Resource.class     //直接使用类名.class
Class.forName("zhang.Resource")  //包名.类名   需要捕获异常
new Resource().getClass()    //先获取一个对象  再使用getCLass()方法

现在我们写五个语句,前两个没/ 后三个有/

System.out.println(Resource.class.getResource(""));    
System.out.println(Resource.class.getResource("Resource.class")); 
System.out.println(Resource.class.getResource("/"));    
System.out.println(Resource.class.getResource("/Resource.class"));  
System.out.println(Resource.class.getResource("/zhang/Resource.class"));

返回的地址都是绝对地址

file:/C:/Users/java/target//target/classes/zhang/
file:/C:/Users/java/target//target/classes/zhang/Resource.
file:/C:/Users/java/target//target/classes/
null
file:/C:/Users/java/target//target/classes/zhang/Resource.class

现在我们来分析 按ctrl + 鼠标左键点进去看源码

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);
}

我们可以看到传进去的name(想要加载的文件的相对地址)被resolveName()方法调用并再次赋值给了name
所以我们要分析下resolveName这个方法

private String resolveName(String name) {
    if (name == null) {
        return name;
    }
    if (!name.startsWith("/")) {		//不以”/”开头
        Class<?> c = this;				//得到  class zhang.Resource
        while (c.isArray()) {
            c = c.getComponentType();
        }
        String baseName = c.getName();	//得到    zhang.Resource 也就是去掉了class
/*  来看下getName()的源码
    public String getName() {
        String name = this.name;     //这里的this.name 就是 zhang.Resource  
        if (name == null)
            this.name = name = getName0();
        return name;
    }
    
*/
        int index = baseName.lastIndexOf('.');   //返回最后一次出现.位置的索引
        if (index != -1) {
            name = baseName.substring(0, index).replace('.', '/')  //拼接 zhang/想要加载的文件的相对地址
                +"/"+name;
        }
    } else {
        name = name.substring(1);   //以“/”开头  去掉“/”   所以得到的是 /classes/想要加载的文件的相对地址
    }
    return name;    //返回的name都是去掉"/"的
}

通过这个方法,我们可以看出加/比不加/多加了个zhang/的目录(通过Class c = this; 然后c.getName() ),也就是包名的目录
所以
加了/ :classpath根目录下 /classes/
没加/ :字节码对象所在目录下 /classes/zhang/ (多了个zhang/)
classpath: 指的是编译后的class文件、xml、properties等配置文件所在的目录 如下图
java的getResource方法_第1张图片
加了/就是classes下面的 ,没加/就是classes/zhang下面的

接下来再来看看后面的代码

public java.net.URL getResource(String name) {
    name = resolveName(name);
    ClassLoader cl = getClassLoader0();   //获取加载该Class的ClassLoader
    if (cl==null) {		//如果加载该Class的ClassLoader为null,则表示这是一个系统class
        // A system class.		系统类加载器
        return ClassLoader.getSystemResource(name);//调用ClassLoader的getSystemResource方法
    }
    return cl.getResource(name);//调用ClassLoader的getResource方法
}

所以getResource最终调用的是ClassLoader的getSystemResource或者getResource方法
现在我们来分析ClassLoader类里面的getResource方法

 public URL getResource(String name) {
     URL url;
     if (parent != null) {
         url = parent.getResource(name);	 //递归调用
     } else {
         url = getBootstrapResource(name);   //启动类加载器
     }
     if (url == null) {  //系统启动类加载器没有加载到,递归回退到第一次调用然后是扩展类加载器
         url = findResource(name);
     }
     return url;
 }

这里涉及到一个知识点 :双亲委派机制
就是如果一个类加载器(用来加载 class 文件)收到了类加载的请求,首先不会自己尝试去加载这个类,二十把这个请求委派给父类加载器去完成。只有当父类加载器无返完成这个加载请求,子加载起才会尝试自己去完成加载java的getResource方法_第2张图片
Java中的类加载器主要分为以下四类:

(1)根类加载器(BootStrapClassLoader), 主要负责加载jre/lib/rt.jar相关的字节码文件的。

(2)扩展类加载器(ExtensionClassLoader), 主要负载加载 jre/lib/ext/*.jar 这些jar包的。 该类加载器在JDK1。9的时候更名为: Platform Class Loader, 其父类加载器为: null。

(3)应用程序类加载器(ApplicationClassLoader), 主要负责加载用户自定义的类以及classpath环境变量所配置的jar包的。 该类加载器在JDK1.9的时候更名为: System ClassLoader, 其父类加载器为: ExtensionClassLoader。

(4)自定义类加载器(UserClassLoader), 负责加载程序员指定的特殊目录下的字节码文件的。大多数情况下,自定义类加载器只需要继承ClassLoader这个抽象类,重写findClass()和loadClass()两个方法即可。
java的getResource方法_第3张图片

另外Class.getResource和ClassLoader.getResource的区别,就是在加载资源文件的时候,加载方式的不同Class.getResource多了个resolveName方法
Class.getResource最后返回的name都是去掉/的 也就是说你使用ClassLoader.getResource方法时候加了/最后只会返回null

参考
彻底搞懂Class.getResource和ClassLoader.getResource的区别和底层原理
java有几种类加载器?工作原理是什么?

你可能感兴趣的:(Java)