大家都知道类初始化是在类的连接后执行的,类的生命周期如下图所示:
初始化是执行类构造器clinit()方法的过程。 clinit()方法是由编译器自动收集类中的所有类变量(被static修饰的变量)和静态代码块(static{}块)中的语句合并产生的。 所以验证类有没有被初始化就可以看它的静态块有没有执行。
下面的代码是打印已加载类的方法,示例中会用到,查看类是否已经被加载
public class PrintClassInfo {
public static void printLoadedClass(String packageNameFilter)throws Exception{
if(packageNameFilter==null){
packageNameFilter="classinitdemo";
}
final String condition=packageNameFilter;
ClassLoader classLoader=Thread.currentThread().getContextClassLoader();
Class loader=ClassLoader.class;
Field classesFiled = loader.getDeclaredField("classes");
classesFiled.setAccessible(true);
Vector<Class> classVector = (Vector<Class>)classesFiled.get(classLoader);
List<Class> classes=new ArrayList<>(classVector);
classes = classes.stream().filter(clazz->clazz.getName().contains(condition)).collect(Collectors.toList());
System.out.println("加载的类:");
classes.forEach(clazz-> System.out.println(clazz.getName()));
}
}
代码中引用类而不会初始化该类有以下几种情况
- 通过子类引用父类的静态字段,只会触发父类的初始化,而不会触发子类的初始化。子类和父类都会被加载。
public class B {
public static int b = 10;
static {
System.out.println("B初始化");
}
}
public class A extends B{
static {
System.out.println("A初始化");
}
}
public class Test {
public static void main(String[] args) {
System.out.println(A.b);
}
}
输出:
B初始化
10
加载的类:
me.ffulauh.javalang.jvm.classinitdemo.PrintClassInfo
me.ffulauh.javalang.jvm.classinitdemo.Test
me.ffulauh.javalang.jvm.classinitdemo.B
me.ffulauh.javalang.jvm.classinitdemo.A
A, B都会被加载,只有B被初始化。
- 定义对象数组,不会触发该类的初始化。该类会加载
B[] bs=new B[10];
输出:
加载的类:
me.ffulauh.javalang.jvm.classinitdemo.PrintClassInfo
me.ffulauh.javalang.jvm.classinitdemo.Test
me.ffulauh.javalang.jvm.classinitdemo.B
- 常量在编译期间会存入调用类的常量池中,本质上并没有直接引用定义常量的类,不
会触发定义常量所在的类。也不会加载常量所在的类。 运行期间确定值得常量除外
public static final int NUM = new Random().randomInt();
调用上述常量就会触发类的初始化
public class Consts {
public static final String HAN="CaptHua";
static {
System.out.println("Consts初始化");
}
}
public class Test extends PrintClassInfo{
public static void main(String[] args) throws Exception{
System.out.println(Consts.HAN);
printLoadedClass(null);
}
}
输出:
CaptHua
加载的类:
me.ffulauh.javalang.jvm.classinitdemo.PrintClassInfo
me.ffulauh.javalang.jvm.classinitdemo.Test
public class Test extends PrintClassInfo{
public static void main(String[] args) throws Exception{
System.out.println(Consts.NUM);
printLoadedClass(null);
}
}
输出:
Consts初始化
625281582
加载的类:
me.ffulauh.javalang.jvm.classinitdemo.PrintClassInfo
me.ffulauh.javalang.jvm.classinitdemo.Test
me.ffulauh.javalang.jvm.classinitdemo.Consts
- 通过类名获取 Class 对象,不会触发类的初始化,会加载。
public class Test extends PrintClassInfo{
public static void main(String[] args) throws Exception{
System.out.println(B.class);
printLoadedClass(null);
}
}
输出:
class me.ffulauh.javalang.jvm.classinitdemo.B
加载的类:
me.ffulauh.javalang.jvm.classinitdemo.PrintClassInfo
me.ffulauh.javalang.jvm.classinitdemo.Test
me.ffulauh.javalang.jvm.classinitdemo.B
- 通过 Class.forName 加载指定类时,如果指定参数 initialize 为 false 时,也不会触
发类初始化,其实这个参数是告诉虚拟机,是否要对类进行初始化。会加载类。
public class Test extends PrintClassInfo{
public static void main(String[] args) throws Exception{
Class.forName("me.ffulauh.javalang.jvm.classinitdemo.B",false,Thread.currentThread().getContextClassLoader());
printLoadedClass(null);
}
}
输出:
加载的类:
me.ffulauh.javalang.jvm.classinitdemo.PrintClassInfo
me.ffulauh.javalang.jvm.classinitdemo.Test
me.ffulauh.javalang.jvm.classinitdemo.B
- 通过 ClassLoader 默认的 loadClass 方法,也不会触发初始化动作(加载了,但是
不初始化)。
public class Test extends PrintClassInfo{
public static void main(String[] args) throws Exception{
ClassLoader classLoader=Thread.currentThread().getContextClassLoader();
classLoader.loadClass("me.ffulauh.javalang.jvm.classinitdemo.B");
printLoadedClass(null);
}
}
输出:
加载的类:
me.ffulauh.javalang.jvm.classinitdemo.PrintClassInfo
me.ffulauh.javalang.jvm.classinitdemo.Test
me.ffulauh.javalang.jvm.classinitdemo.B
|