类加载过程

原文来自《码出高效: Java 开发手册》

代码地址: easy-coding

Java 的类加载器是一个运行时核心基础设施模块,如图 4-4 所示,主要是在启动之初进行类的 Load、Link 和 Init,即加载、链接、初始化。

第一步,Load 阶段读取类文件产生二进制流,并转换为特定的数据结构,初步校验 cafe babe 魔法数、常量池、文件长度、是否有父类等,然后创建对应类的 java.lang.Class 实例。

第二步,Link 阶段包括验证、准备、解析三个步骤。验证是更详细的校验,比如 final 是否合规、类型是否正确、静态变量是否合理等;准备阶段是为静态变量分配内存,并设定默认值;解析阶段是解析类和方法,确保类与类之间的相互引用正确性,完成内存结构布局。

第三步,Init 阶段执行类构造器 <clinit> 方法,如果赋值运算是通过其他类的静态方法来完成的,那么会马上解析另一个类,在虚拟机栈中执行完毕后通过返回值进行赋值。

代码

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
45
46
47
48
public class ClassTest {
// 数组类型有一个魔法属性: length 来获取数组长度
private static int[] array = new int[3];
private static int length = array.length;

// 任何小写 class 定义的类,也有一个魔法属性: class,来获取此类的大写 Class 类对象
private static Class<One> one = One.class;
private static Class<Another> another = Another.class;

public static void main(String[] args) throws Exception {
// 通过newInstance 方法创建 One 和 Another 的类对象( 第 1 处 )
One oneObject = one.newInstance();
oneObject.call();

Another anotherObject = another.newInstance();
anotherObject.speak();

// 通过 one 这个大写的 Class 对象,获取私有成员属性对象 Field( 第 2 处 )
Field privateFieldInOne = one.getDeclaredField("inner");

// 设置私有对象可以访问和修改( 第 3 处 )
privateFieldInOne.setAccessible(true);

privateFieldInOne.set(oneObject, "world changed.");
// 成功修改类的私有属性 inner 变量值为 world changed.
System.out.println(oneObject.getInner());
}


}

class One {
private String inner = "time files.";

public void call() {
System.out.println("hello world.");
}

public String getInner() {
return inner;
}
}

class Another {
public void speak() {
System.out.println("easy coding.");
}
}

代码

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
public class TestWhoLoad {

public static void main(String[] args) {
// 正在使用的类加载器: jdk.internal.loader.ClassLoaders$AppClassLoader@69d0a
ClassLoader c = TestWhoLoad.class.getClassLoader();
// AppClassLoader 的类加载器是 PlatformClassLoader,即平台类加载器
ClassLoader c1 = c.getParent();
// PlatformClassLoader 的父加载器是 Bootstrap。它是使用 C++ 来实现的,返回 null
ClassLoader c2 = c1.getParent();

System.out.println(c);
System.out.println(c1);
System.out.println(c2);

// 通过以下代码可以查看 Bootstrap 所有已经加载的类库
URL[] urLs = Launcher.getBootstrapClassPath().getURLs();
for (URL urL : urLs) {
System.out.println(urL.toExternalForm());
}
}
}/*
jdk8 output:
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@61bbe9ba
null

jdk11 output:
jdk.internal.loader.ClassLoaders$AppClassLoader@7c53a9eb
jdk.internal.loader.ClassLoaders$PlatformClassLoader@2752f6e2
null
*/
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
public class CustomClassLoader extends ClassLoader{
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] result = getClassFromCustomPath(name);
if (result == null) {
throw new FileNotFoundException();
} else {
return defineClass(name, result, 0, result.length);
}
} catch (Exception e) {
e.printStackTrace();
}
throw new ClassNotFoundException(name);
}

private byte[] getClassFromCustomPath(String name) {
// 从自定义路径中加载指定类
return null;
}

public static void main(String[] args) {
CustomClassLoader customClassLoader = new CustomClassLoader();
try {
Class<?> clazz = Class.forName("One", true, customClassLoader);
Object obj = clazz.newInstance();
System.out.println(obj.getClass().getClassLoader());
} catch (Exception e) {
e.printStackTrace();
}
}
}/* output:
classloader.CustomClassLoader@5e481248
*/
打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2015-2023 高行行
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信