Fork me on GitHub

浅谈Class.forName()和classLoader.loadClass()的区别

在理解这两个区别前,需要弄清楚java类的加载机制。

类加载机制

  1. 加载。加载过程完成以下三件事:
    • 通过类的完全限定名称获取定义该类的二进制字节流。
    • 将该字节流表示的静态存储结构转换为方法区的运行时存储结构。
    • 在内存中生成一个代表该类的 Class 对象,作为方法区中该类各种数据的访问入口。
  2. 验证。确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
  3. 准备。类变量是被 static 修饰的变量,准备阶段为类变量分配内存并设置初始值(一般为 0falsenull),使用的是方法区的内存。实例变量不会在这阶段分配内存,它会在对象实例化时随着对象一起被分配在堆中。应该注意到,实例化不是类加载的一个过程,类加载发生在所有实例化操作之前,并且类加载只进行一次,实例化可以进行多次。特别的,类静态常量直接设置值。
  4. 解析。将常量池的符号引用替换为直接引用的过程。其中解析过程在某些情况下可以在初始化阶段之后再开始,这是为了支持 Java 的动态绑定。
  5. 初始化。初始化阶段才真正开始执行类中定义的 Java 程序代码。初始化阶段是虚拟机执行类构造器 <clinit>() 方法的过程。在准备阶段,类变量已经赋过一次系统要求的初始值,而在初始化阶段,根据程序员通过程序制定的主观计划去初始化类变量和其它资源。

区别

二者均是调用了 ClassLoadloadClass(String name, boolean resolve) 方法。

不同的是 参数 resolve 取值不同。 Class.forName(String name)trueloadClass(String name)false

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/**
* Loads the class with the specified <a href="#binary-name">binary name</a>. The
* default implementation of this method searches for classes in the
* following order:
*
* <ol>
*
* <li><p> Invoke {@link #findLoadedClass(String)} to check if the class
* has already been loaded. </p></li>
*
* <li><p> Invoke the {@link #loadClass(String) loadClass} method
* on the parent class loader. If the parent is {@code null} the class
* loader built into the virtual machine is used, instead. </p></li>
*
* <li><p> Invoke the {@link #findClass(String)} method to find the
* class. </p></li>
*
* </ol>
*
* <p> If the class was found using the above steps, and the
* {@code resolve} flag is true, this method will then invoke the {@link
* #resolveClass(Class)} method on the resulting {@code Class} object.
*
* <p> Subclasses of {@code ClassLoader} are encouraged to override {@link
* #findClass(String)}, rather than this method. </p>
*
* <p> Unless overridden, this method synchronizes on the result of
* {@link #getClassLoadingLock getClassLoadingLock} method
* during the entire class loading process.
*
* @param name
* The <a href="#binary-name">binary name</a> of the class
*
* @param resolve
* If {@code true} then resolve the class
*
* @return The resulting {@code Class} object
*
* @throws ClassNotFoundException
* If the class could not be found
*/

protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}

if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);

// this is the defining class loader; record the stats
PerfCounter.getParentDelegationTime().addTime(t1 - t0);
PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}

resolve默认是false不链接,不进行链接意味着不进行上述2、3、4步骤,那么静态块和静态对象就不会得到执行。

测试

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
public class Test{
public static void main(String[] args) {
try {
System.out.println("1-----------------------");
ClassLoader.getSystemClassLoader().loadClass("X");
System.out.println("2-----------------------");
ClassLoader.getSystemClassLoader().loadClass("X");
System.out.println("3-----------------------");
ClassLoader.getSystemClassLoader().loadClass("X");
System.out.println("4-----------------------");
Class.forName("X");
System.out.println("5-----------------------");
Class.forName("X");
System.out.println("6-----------------------");
Class.forName("X");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}

class X{
static{
System.out.println("hello");
}
}

输出为:

1
2
3
4
5
6
7
1-----------------------
2-----------------------
3-----------------------
4-----------------------
hello
5-----------------------
6-----------------------
扫描二维码,拯救贫困山区大学生!
-------------本文结束感谢您的阅读-------------