Java类加载器(一)
Java默认提供的三个ClassLoader
BootStrap ClassLoader
BootStrap ClassLoader
称为启动类加载器。用来加载JDK核心类库。来看一下,BootStrap ClassLoader
类加载器从哪些地方加载了相关的jar或class文件
URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (URL url : urls) {
System.out.println(url);
}
执行结果如下:
file:/C:/Program%20Files/Java/jdk1.8.0_111/jre/lib/resources.jar
file:/C:/Program%20Files/Java/jdk1.8.0_111/jre/lib/rt.jar
file:/C:/Program%20Files/Java/jdk1.8.0_111/jre/lib/sunrsasign.jar
file:/C:/Program%20Files/Java/jdk1.8.0_111/jre/lib/jsse.jar
file:/C:/Program%20Files/Java/jdk1.8.0_111/jre/lib/jce.jar
file:/C:/Program%20Files/Java/jdk1.8.0_111/jre/lib/charsets.jar
file:/C:/Program%20Files/Java/jdk1.8.0_111/jre/lib/jfr.jar
file:/C:/Program%20Files/Java/jdk1.8.0_111/jre/classes
上述结果也可以通过查找sun.boot.class.path这个系统属性所得知
System.out.println(System.getProperty("sun.boot.class.path"));
Extension ClassLoader
Extension ClassLoader
称为拓展类加载器。负责加载Java的拓展类库;默认情况下,加载JAVA_HOME/jre/lib/ext/
目录下的所有jar
App ClassLoader
App ClassLoader
称为系统类加载器,负责加载应用程序classpath目录下所有的jar和class文件
获取某个类由哪个类加载器加载
class Member {}
public class TestDemo {
public static void main(String[] args) {
Class<?> cls = Member.class;
System.out.println(cls.getClassLoader());
System.out.println(cls.getClassLoader().getParent());
System.out.println(cls.getClassLoader().getParent().getParent());
}
}
上面代码的运行结果
sun.misc.Launcher$AppClassLoader@232204a1
sun.misc.Launcher$ExtClassLoader@74a14482
null
下面的代码纯属演示,演示loadClass(String className)方法,没有实际意义
class Member {
@Override
public String toString() {
return "Member{}";
}
}
public class TestDemo {
public static void main(String[] args) throws Exception{
System.out.println(Class.forName("cn.cecurio.load.Member").
getClassLoader().loadClass("cn.cecurio.load.Member").newInstance());
}
}
自定义类加载器,加载本机文件
自定义的ClassLoader都必须继承自java.lang.ClassLoader类。
Extension ClassLoader
和App ClassLoader
都继承java.lang.ClassLoader类。但是Bootstrap ClassLoader
不继承自java.lang.ClassLoader
,因为它不是一个普通的Java类,底层由C++编写,已嵌入到了JVM内核当中,当JVM启动后,Bootstrap ClassLoader
也随着启动,负责加载完核心类库后,并构造Extension ClassLoader
和App ClassLoader
类加载器。
第一步: 在本机非项目CLASSPATH位置新建.java文件,并编译
名称是Member.java,内容如下:
package cn.cecurio.vo;
public class Member {
public String toString() {
return "Member in cn.cecurio.vo";
}
}
第二步: 定义自己的类加载器
public class MyClassLoader extends ClassLoader {
/**
* 实现一个自定义的类加载器,传入类的全限定名,通过读取指定文件来加载类
* @param className
* @return 类的Class对象
*/
public Class<?> loadData(String className) {
// 加载类文件的数据信息
byte[] classData = this.loadClassData();
return super.defineClass(className,classData,0,classData.length);
}
/**
* 通过指定类的文件路径,进行类的加载,其实就是读取二进制文件
* @return 类文件的数据信息
*/
private byte[] loadClassData() {
try {
File inFile = new File("D:" + File.separator + "tmp"
+ File.separator + "Member.class");
InputStream inputStream = new FileInputStream(inFile);
// 可以有一个方法取得所有的字节内容
ByteArrayOutputStream bos = new ByteArrayOutputStream();
// 定义读取的缓冲区
byte[] data = new byte[512];
int len = 0;
while ((len = inputStream.read(data)) != -1) {
bos.write(data,0,len);
}
byte[] ret = bos.toByteArray();
inputStream.close();
bos.close();
return ret;
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
可以自己修改上述代码中,文件路径硬编码的地方
测试自己的类加载器
public class Main {
public static void main(String[] args) {
MyClassLoader loader = new MyClassLoader();
Class<?> cls = loader.loadData("cn.cecurio.vo.Member");
try {
System.out.println(cls.newInstance());
System.out.println(cls.getClassLoader());
System.out.println(cls.getClassLoader().getParent());
System.out.println(cls.getClassLoader().getParent().getParent());
System.out.println(cls.getClassLoader().getParent().getParent().getParent());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
输出结果是:
Member in cn.cecurio.vo
cn.cecurio.load.MyClassLoader@1540e19d
sun.misc.Launcher$AppClassLoader@232204a1
sun.misc.Launcher$ExtClassLoader@14ae5a5
null
类加载器的意义是:通过动态的路径实现类加载处理操作。