类加载器
什么是类加载器?
类加载器:类加载器是负责加载类的对象。将class文件(硬盘)加载到内存生成Class对象。所有的类加载器都是 java.lang.ClassLoader 的子类。
解释上图:当我们要用一个类,这个类叫做Hello类,这个类假设不在内存中,我们现在需要用到它了,此时虚拟机就会在内存中说:内存中有没有Hello啊?内存说没有。虚拟机又说:类加载器加载一下这个Hello类,而实际中类加载器有很多;虚拟机会首先叫AppClassLoader(应用类加载器)加载一下Hello类,但是AppClassLoader在加载这个Hello类之前会去找它的父加载器ExtClassLoader(扩展类加载器)去问一下:你加载了这个Hello类吗?此时ExtClassLoader说等下,我去问一下父类BootstrapClassLoader(引导类加载器)有没有加载这个Hello类?ExtClassLoader说我没有加载这个Hello类,那么ExtClassLoader得到消息后就回复AppClassLoader类说我没有加载这个类。此时AppClassLoader类会自己去加载这个Hello类,我们就可以使用这个Hello类了。如果BootstrapClassLoader说我已经加载了Hello类了,那么就会对ExtClassLoader说:我已经加载了,拿去用,ExtClassLoader对AppClassLoader说我加载了,拿去用吧,AppClassLoader直接使用。
综上可得知:一个类在我们的虚拟机中,只允许被加载一次。
我们可以使用:类.class.getClassLoader()获得加载自己的类加载器。
那么我们现在就遇到一个问题:第一个类是从哪里来的?答:在JVM中有那么一个程序它不是java编写的,它是由c、c++编写的。我们称之为引导类加载器,这个是给它的名称而不是具体的类,所以它不是类,它是一个null,是一个空对象。
类加载器加载机制:全盘负责委托机制。
全盘负责:A类如果要使用B类(B类不在内存中),负责加载A类的加载器就需要负责把B类加载进来。谁用谁加载。
委托机制:A类加载器如果要加载资源B,必须询问父类加载是否加载。
如果加载,将直接使用。
如果没有机制,自己再加载。
采用 全盘负责委托机制 保证 一个class文件 只会被加载一次,形成一个Class对象
注意:
如果一个class文件,被两个类加载器加载,将是两个对象。(理解成JDK加载了一次,另一个程序也加载了一次)
第一个加载器是A:h.getClass() -->A 第二个加载器是B: h.getClass() -->B
很有可能出现有提示 com.umanwu.Hello 不能强制成 com.umanwu.Hello
会出现在自定义类加载出现可以将一个class文件加载多次。
类加载的演示:
查看引导类加载器的内容:
package com.umanwu.classloader;
import org.junit.Test;
public class ClassLoaderDemo {
@Test
public void demo01() {
// 查看引导类加载器加载的内容
// * JDK固定配置信息,sun.boot.class.path 用于表示 引导类加载器所加载的内容
String paths = System.getProperty("sun.boot.class.path");
String[] allPath = paths.split(";");
for (String p : allPath) {
System.out.println(p);
}
}
}
输出的结果是:
查看引导类加载器加载的类型:
package com.umanwu.classloader;
import org.junit.Test;
public class ClassLoaderDemo {
@Test
public void demo02(){
// 查看引导类加载器加载的类型
ClassLoader cl = String.class.getClassLoader();
System.out.println(cl);
}
}
输出的结果是:null
查看扩展类加载器的内容:
package com.umanwu.classloader;
import org.junit.Test;
import sun.net.spi.nameservice.dns.DNSNameService;
public class ClassLoaderDemo_02 {
@Test
public void demo01(){
// 查看扩展类加载器加载的内容
// * JDK固定配置信息,java.ext.dirs 用于表示 扩展类加载器所加载的内容
String paths = System.getProperty("java.ext.dirs");
String[] allPath = paths.split(";");
for(String p : allPath){
System.out.println(p);
}
}
}
输出:
查看扩展类加载器的类型:
package com.umanwu.classloader;
import org.junit.Test;
import sun.net.spi.nameservice.dns.DNSNameService;
public class ClassLoaderDemo_02 {
@Test
public void demo02(){
// 查看扩展类加载器的类型:Launcher$ExtClassLoader($表示匿名内部类)
ClassLoader cl = DNSNameService.class.getClassLoader();//JDK中随便找一个类的,需要添加允许访问规则
System.out.println(cl);
}
}
添加允许访问规则:
输出:aaa@qq.com
查看应用类加载器的内容:
package com.umanwu.classloader;
import org.junit.Test;
public class ClassLoaderDemo_03 {
@Test
public void demo01(){
// 查看应用类加载器加载的内容: 项目/bin (编译后内容) ,自己编写类由应用类加载加载
String paths = System.getProperty("java.class.path");
String[] allPath = paths.split(";");
for(String p : allPath){
System.out.println(p);
}
}
}
输出:
查看应用类加载器加载的类型:
package com.umanwu.classloader;
import org.junit.Test;
public class ClassLoaderDemo_03 {
@Test
public void demo02(){
ClassLoader cl = ClassLoaderDemo_03.class.getClassLoader();
System.out.println(cl);
}
}
输出:aaa@qq.com三个加载器之间的关系:
package com.umanwu.classloader;
import org.junit.Test;
public class ClassLoaderDemo_04 {
@Test
public void demo01(){
//3个类加载的关系
ClassLoader c1 = ClassLoaderDemo_04.class.getClassLoader();
System.out.println(c1); //应用 (AppClassLoader)
ClassLoader c2 = c1.getParent();
System.out.println(c2); //扩展(ExtClassLoader)
ClassLoader c3 = c2.getParent();
System.out.println(c3); //引导(null)
}
}
输出:
aaa@qq.com
aaa@qq.com
null
扩展:
//获得所有属性配置
Properties properties = System.getProperties();
for (String string : properties.stringPropertyNames()) {
System.out.println(string);
}
//引导:sun.boot.class.path 用于表示引导类加载器所加载的内容
//扩展:java.ext.dirs 用于表示扩展类加载器所加载的内容
//应用:java.class.path 用于表示应用加载器所加载的内容