一 点睛
除了类的主动使用,其他的情况均属于类的被动使用。被动使用不会引起类的初始化。也就是说:并不是在代码中出现的类,就一定会被加载或者初始化。如果不符合主动使用的条件,类就不会初始化。
1 静态字段
当通过子类引用父类的静态变量,不会导致子类初始化,只有真正声明这个字段的类才会被初始化。
2 数组定义
通过数组定义类引用,不会触发此类的初始化
3 引用常量
引用常量不会触发此类或接口的初始化。因为常量在链接阶段就已经被显式赋值了。
4 loadClass 方法
调用 ClassLoader 类的 loadClass()方法加载一个类,并不是对类的主动使用,不会导致类的初始化。
二 扩展
-XX:+TraceClassLoading:追踪打印类的加载信息
三 实战1
1 代码
package chapter03.java1;
import org.junit.Test;
/**
*
* 关于类的被动使用,即不会进行类的初始化操作,即不会调用<clinit>()
* 说明:没有初始化的类,不意味着没有加载!
*/
public class PassiveUse1 {
/**
* 1. 当访问一个静态字段时,只有真正声明这个字段的类才会被初始化。
* > 当通过子类引用父类的静态变量,不会导致子类初始化
*/
@Test
public void test1(){
System.out.println(Child.num);
// 输出情况
// Parent的初始化过程 -> 子类不需要初始化
// 1
}
/**
* 2. 通过数组定义类引用,不会触发此类的初始化
*/
@Test
public void test2(){
Parent[] parents = new Parent[10]; // 不打印 —> 没有初始化,运行时才会加载进来
System.out.println(parents.getClass()); // class [Lchapter03.java1.Parent;
System.out.println(parents.getClass().getSuperclass()); // class java.lang.Object
parents[0] = new Parent(); // Parent的初始化过程 -> 类初始化
parents[1] = new Parent(); // 不打印 -> 类已经初始化过了
}
}
class Parent{
static{
System.out.println("Parent的初始化过程");
}
public static int num = 1;
}
class Child extends Parent{
static{
System.out.println("Child的初始化过程");
}
}
2 执行 test1
Parent的初始化过程
1
3 执行 test2
class [Lchapter03.java1.Parent;
class java.lang.Object
Parent的初始化过程
四 实战2
1 代码
package chapter03.java1;
import org.junit.Test;
import java.util.Random;
public class PassiveUse2 {
/**
* 3. 引用常量不会触发此类或接口的初始化。因为常量在链接阶段就已经被显式赋值了。
*/
@Test
public void test1(){
// System.out.println(Person.NUM); // test1_1:不会触发 Person 类的初始化
// System.out.println(Person.NUM1); // test1_1:触发了 Person 类的初始化
}
@Test
public void test2(){
// System.out.println(SerialA.ID); // test2_1:接口同理 使用SerialA.ID 不会触发 SerialA 的初始化
// System.out.println(SerialA.ID1); // test2_2:接口同理 ID1 会触发 SerialA 的初始化
}
/**
* 4. 调用 ClassLoader 类的 loadClass() 方法加载一个类,并不是对类的主动使用,不会导致类的初始化。
*/
@Test
public void test3(){
try {
// 不会触发 Person 类的初始化,被动使用
Class clazz = ClassLoader.getSystemClassLoader().loadClass("chapter03.java1.Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class Person{
static{
System.out.println("Person类的初始化");
}
public static final int NUM = 1; // 在链接过程的准备环节就被赋值为1了。
public static final int NUM1 = new Random().nextInt(10); // 此时的赋值操作需要在<clinit>() 中执行
}
interface SerialA{
public static final Thread t = new Thread() {
{
System.out.println("SerialA的初始化");
}
};
int ID = 1;
int ID1 = new Random().nextInt(10); // 此时的赋值操作需要在<clinit>()中执行
}
2 测试
只运行 test1_1:
1
只运行 test1_2:
Person类的初始化
1
只运行 test2_1:
1
只运行 test2_2:
SerialA的初始化
1
运行 test3
无打印