类加载器是用来加载class文件的,通常情况下,我们使用的类是由虚拟机自动调用类加载器进行加载,而对于用户类使用的应该是AppClassLoader.loadClass方法进行加载的。
但是在我尝试自己调用AppClassLoader对象的loadClass方法时,却发现跟系统自动加载时行为上有些不一致。
看下面这段代码:- import java.lang.reflect.Field;
- import java.util.Scanner;
- class ClassLoaderTest
- {
- public static void main(String[] args)
- throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException{
- ClassLoader loader = ClassLoaderTest.class.getClassLoader();//获取AppClassLoader对象
- System.out.println("获取到的ClassLoader对象为:" + loader.getClass().getName());
- //Output:获取到的ClassLoader对象为:sun.misc.Launcher$AppClassLoader
- //证明获取到的确实是AppClassLoader
-
- //自动加载类
- //Class cls2 = Class.forName("StaticPrint"); //直接加载对象并进行静态初始化动作
-
- //手动加载类
- Class cls = loader.loadClass("StaticPrint"); //throws ClassNotFoundException
- System.out.println("被加载的Class对象的名称为:" + cls.getName());
- //System.out.println(cls == cls2);
- System.out.println("等一等,我去删class文件...");
- new Scanner(System.in).nextLine(); //阻塞式方法,用此时间去把文件夹下编译好的StaticPrint.class删除
- Field field = cls.getField("INFO"); //throws NoSuchFieldException
- System.out.println("Field对象获取完成");
- System.out.println("StaticPrint.INFO = " + field.get(null));//到这一步才进行静态初始化动作,throws IllegalAccessException
- }
- }
- class StaticPrint
- {
- public static final String INFO = "what a fucking bug!";
- static{
- System.out.println("StaticPrint loading...");
- }
- }
复制代码 在这段代码中,使用获取到了AppClassLoader对象,并调用loadClass方法来加载了自定义的StaticPrint类,这个类中包含了一个static块。按照以前的知识,类成员将在类被加载时初始化,static块将在类被加载时执行并输出StaticPrint loading...,然而在这里调用了loadClass方法后却并没有立刻输出,而是在最后一条语句反射INFO属性时才输出,但是因为执行完loadClass方法后,在阻塞方法nextLine()的阻塞期中,我去尝试删除了StaticPrint.class文件,那么如果loadClass方法没有加载成功,则不可能反射出其INFO属性,也不可能输出那条static块中的语句。既然输出了,说明他确实在loadClass方法执行后就加载进内存了。
这样就产生了一个问题,类被加载了,但是却没有进行显式的静态初始化动作,这和以前的知识似乎有点不符。
此时我只能这样认为:类在加载时并没有进行静态初始化,而是在静态成员被访问时才进行静态初始化。这样解释似乎合情合理。
但是,在进行了另一项尝试后,又陷入了困惑。
大家应该注意到我上面的代码中,有两行注释掉的自动加载类的代码,Class cls2 = Class.forName("StaticPrint");
这条代码直接调用Class.forName方法来产生StaticPrint的Class对象,而在这个过程中,类被自动加载了。这点很容易理解,但是更重要的是,static块中的语句被执行并输出了。这意味着StaticPrint被加载并静态初始化了!与手动调用loadClass方法产生了完全不同的结果!二者同样是使用AppClassLoader进行加载,那么应该都是调用了loadClass方法才对,并且同样只是返回了一个StaticPrint的Class对象,而没有进行静态成员访问,但是系统自动调用却直接进行了static初始化!
纠结了好久,也没想明白为啥会这样。请问这到底是为什么~? |