Java类加载器(一)

初步理解Java类加载机制

在程序启动的时候,并不会一次性加载程序所需的所有.class文件,而是根据程序的需要,通过ClassLoader来动态加载某个.class文件到内存中;只有.class文件在内存中,才能被其他class所引用。所以,ClassLoader就是用来动态地加载.class文件到内存中

Java默认提供的三个ClassLoader

BootStrap ClassLoader

BootStrap ClassLoader称为启动类加载器。用来加载JDK核心类库。来看一下,BootStrap ClassLoader类加载器从哪些地方加载了相关的jar或class文件

1
2
3
4
URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (URL url : urls) {
System.out.println(url);
}

执行结果如下:

1
2
3
4
5
6
7
8
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这个系统属性所得知

1
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文件

获取某个类由哪个类加载器加载

1
2
3
4
5
6
7
8
9
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());
}
}

上面代码的运行结果

1
2
3
sun.misc.Launcher$AppClassLoader@232204a1
sun.misc.Launcher$ExtClassLoader@74a14482
null

下面的代码纯属演示,演示loadClass(String className)方法,没有实际意义

1
2
3
4
5
6
7
8
9
10
11
12
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 ClassLoaderApp ClassLoader都继承java.lang.ClassLoader类。但是Bootstrap ClassLoader不继承自java.lang.ClassLoader,因为它不是一个普通的Java类,底层由C++编写,已嵌入到了JVM内核当中,当JVM启动后,Bootstrap ClassLoader也随着启动,负责加载完核心类库后,并构造Extension ClassLoaderApp ClassLoader类加载器。

第一步: 在本机非项目CLASSPATH位置新建.java文件,并编译

名称是Member.java,内容如下:

1
2
3
4
5
6
7
package cn.cecurio.vo;
public class Member {
public String toString() {
return "Member in cn.cecurio.vo";
}
}

第二步: 定义自己的类加载器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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;
}
}
}

可以自己修改上述代码中,文件路径硬编码的地方

测试自己的类加载器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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();
}
}
}

输出结果是:

1
2
3
4
5
Member in cn.cecurio.vo
cn.cecurio.load.MyClassLoader@1540e19d
sun.misc.Launcher$AppClassLoader@232204a1
sun.misc.Launcher$ExtClassLoader@14ae5a5
null

类加载器的意义是:通过动态的路径实现类加载处理操作。

If you think the content is useful to you.