A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© longlangcx 中级黑马   /  2013-8-3 20:43  /  1346 人查看  /  4 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

类加载器是用来加载class文件的,通常情况下,我们使用的类是由虚拟机自动调用类加载器进行加载,而对于用户类使用的应该是AppClassLoader.loadClass方法进行加载的。

但是在我尝试自己调用AppClassLoader对象的loadClass方法时,却发现跟系统自动加载时行为上有些不一致。
看下面这段代码:
  1. import java.lang.reflect.Field;
  2. import java.util.Scanner;

  3. class ClassLoaderTest
  4. {
  5.         public static void main(String[] args)
  6.                 throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException{

  7.                 ClassLoader loader = ClassLoaderTest.class.getClassLoader();//获取AppClassLoader对象
  8.                 System.out.println("获取到的ClassLoader对象为:" + loader.getClass().getName());
  9.                 //Output:获取到的ClassLoader对象为:sun.misc.Launcher$AppClassLoader
  10.                 //证明获取到的确实是AppClassLoader
  11.                
  12.                 //自动加载类
  13.                 //Class cls2 = Class.forName("StaticPrint"); //直接加载对象并进行静态初始化动作
  14.                
  15.                 //手动加载类
  16.                 Class cls = loader.loadClass("StaticPrint");        //throws ClassNotFoundException
  17.                 System.out.println("被加载的Class对象的名称为:" + cls.getName());
  18.                 //System.out.println(cls == cls2);

  19.                 System.out.println("等一等,我去删class文件...");
  20.                 new Scanner(System.in).nextLine();        //阻塞式方法,用此时间去把文件夹下编译好的StaticPrint.class删除
  21.                 Field field = cls.getField("INFO");                //throws NoSuchFieldException
  22.                 System.out.println("Field对象获取完成");
  23.                 System.out.println("StaticPrint.INFO = " + field.get(null));//到这一步才进行静态初始化动作,throws IllegalAccessException

  24.         }
  25. }

  26. class StaticPrint
  27. {
  28.         public static final String INFO = "what a fucking bug!";
  29.         static{
  30.                 System.out.println("StaticPrint loading...");
  31.         }
  32. }
复制代码
在这段代码中,使用获取到了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初始化!

纠结了好久,也没想明白为啥会这样。请问这到底是为什么~?

评分

参与人数 1技术分 +1 收起 理由
神之梦 + 1 神马都是浮云

查看全部评分

4 个回复

倒序浏览
神马都是浮云,,,

我是来围观的
不过相信姐们儿

一定有人会给你讲的
亲不要忘记在解决此问题后将“未解决”改为“已解决”

{:soso_e121:}

回复 使用道具 举报
本帖最后由 肥猫 于 2013-8-3 22:39 编辑
杨璐敏 发表于 2013-8-3 22:22
神马都是浮云,,,

我是来围观的

妹子,加个Q聊聊呗:hug:我是个刚学JAVA的菜鸟,很多知识想找高手谈论可没人理我.
回复 使用道具 举报
肥猫 发表于 2013-8-3 22:37
妹子,加个Q聊聊呗我是个刚学JAVA的菜鸟,很多知识想找高手谈论可没人理我. ...

我也是菜鸟怎么办:(
回复 使用道具 举报
杨璐敏 发表于 2013-8-3 23:13
我也是菜鸟怎么办

那我们菜鸟手握手,共同进步呗~~>
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马