一道关于ClassLoader的面试题

wingardium 2011-11-17 02:09:12
请看如下代码, 先不要运行, 说出程序会输出什么?


import java.net.*;

public class Andromeda {
private static final Andromeda instance = new Andromeda();

private Andromeda(){

}

public static Andromeda getInstance(){
return instance;
}

public static void main(String[] args) throws Exception{

URLClassLoader loader1 = new URLClassLoader(new URL[]{new URL("file:///home/root/workspace/andromeda/bin/")}){
public String toString(){
return "loader1";
}
};

URLClassLoader loader2 = new URLClassLoader(new URL[]{new URL("file:///home/root/workspace/andromeda/bin/")},
ClassLoader.getSystemClassLoader().getParent()){
public String toString(){
return "loader2";
}
};

Class clazz1 = loader1.loadClass("Andromeda");
Class clazz2 = loader2.loadClass("Andromeda");

Andromeda obj1 = (Andromeda)(clazz1.getDeclaredMethod("getInstance", new Class[0]).invoke(null, new Object[0]));
System.out.println(loader1 + " loads " + obj1);

Andromeda obj2 = (Andromeda)(clazz2.getDeclaredMethod("getInstance", new Class[0]).invoke(null, new Object[0]));
System.out.println(loader2 + " loads " + obj2);

Andromeda obj3 = Andromeda.getInstance();
System.out.println(Andromeda.class.getClassLoader() + " loads " + obj3);
}
}

...全文
236 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
疯狂的驴子 2011-11-17
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 softroad 的回复:]

Bootstrap ClassLoader是在JVM开始运行的时候加载java的核心类,是用C++编写的,它用来加载核心类库
Extension ClassLoader是用来加载扩展类,即/lib/ext中的类。
AppClassLoader用来加载Classpath的类,是和我们关系最密切的类。
URLClassLoader用来加载网络上远程的类,暂且不讨论。

它们之间的关系:
……
[/Quote]
受教了!感谢!!
若鱼1919 2011-11-17
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 wingardium 的回复:]
但是两个被加载的Andromeda类的包名称(都是default)和类名称都完全一致, 那它们是同一个Class对象, 还是两个Class对象呢?
[/Quote]

java里面一个类的类型是由“类的完全限定名”和加载这个类的“类加载器”共同决定的。

类名一样,类加载器不一样的话,也是不同的类。
wingardium 2011-11-17
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 goldenfish1919 的回复:]
执行main函数,会导致加载初始化Andromeda类,这是由AppClassLoader进行加载的。

Andromeda obj2 = (Andromeda)(clazz2***************)
左边的Andromeda是由AppClassLoader载入的,右边的是由loader2载入的,因此,会出现ClassCastException
[/Quote]
但是两个被加载的Andromeda类的包名称(都是default)和类名称都完全一致, 那它们是同一个Class对象, 还是两个Class对象呢?
waiter081 2011-11-17
  • 打赏
  • 举报
回复
那请问从类Andromeda强转到类Andromeda, 会抛出异常呢? 确实会,4楼说的很对,他们的类加载器不同!
softroad 2011-11-17
  • 打赏
  • 举报
回复
Bootstrap ClassLoader
Extension ClassLoader
AppClassLoader
URLClassLoader

loader1的父亲是AppClassLoader
loader2的的父亲ExtClassLoader

两个不是同一个加载器的加载的类

应该是这样的。

softroad 2011-11-17
  • 打赏
  • 举报
回复
Bootstrap ClassLoader是在JVM开始运行的时候加载java的核心类,是用C++编写的,它用来加载核心类库
Extension ClassLoader是用来加载扩展类,即/lib/ext中的类。
AppClassLoader用来加载Classpath的类,是和我们关系最密切的类。
URLClassLoader用来加载网络上远程的类,暂且不讨论。

它们之间的关系:

1.Parent-Child,按顺序从大到小。不是简单的继承关系。

2.ClassLoader有个getParent的方法,但是Ext ClassLoader调用后得到的是null,bootstrap是JVM自己的,用户看不到。

3.classloader的委托机制:当等级比较低的ClassLoader要加载某个类的时候,它首先会请求Parent加载器来加载,Parent再请求它的Parent
比如现在Ext要加载了,它往上请求。如果最大的Bootstrap找不到,那么Boot会叫Ext自己找找,Ext找不到,是不会让下一级的App去找的,此时就报出ClassNotFoundException

4.类A调用类B,B会要求调用它的类的类加载器来加载它,也就是B会要求加载A的加载器来加载B。这就会有个问题,如果他们在一起,那没关系,肯定某个classloader会把它们俩都加载好。但是如果A在/lib/ext文件夹中,而B在Classpath中呢?过程是这样的首先加载A,那么一层层上到Bootstrap Classloader,boot没找到所以ext自己找,找到了,没问题;加载B,因为A调用了B,所以也从bootstrap来找,没找到,然后A的ext classloader来找还是没找到,但是再也不会往下调用了,于是报出ClassNotFoundException。
但是现实生活中有很多应用,比如JDBC核心方法在核心库而驱动在扩展库,是必定在两个地方的,那怎么办呢?要用到Context ClassLoader我们在建立一个线程Thread的时候,可以为这个线程通过setContextClassLoader方法来指定一个合适的classloader作为这个线程的context classloader,当此线程运行的时候,我们可以通过getContextClassLoader方法来获得此context classloader,就可以用它来载入我们所需要的Class。默认的是system classloader。利用这个特性,我们可以“打破”classloader委托机制了,父classloader可以获得当前线程的context classloader,而这个context classloader可以是它的子classloader或者其他的classloader,那么父classloader就可以从其获得所需的 Class,这就打破了只能向父classloader请求的限制了。这个机制可以满足当我们的classpath是在运行时才确定,并由定制的 classloader加载的时候,由system classloader(即在jvm classpath中)加载的class可以通过context classloader获得定制的classloader并加载入特定的class(通常是抽象类和接口,定制的classloader中是其实现),例如web应用中的servlet就是用这种机制加载的.
若鱼1919 2011-11-17
  • 打赏
  • 举报
回复
执行main函数,会导致加载初始化Andromeda类,这是由AppClassLoader进行加载的。

Andromeda obj2 = (Andromeda)(clazz2***************)
左边的Andromeda是由AppClassLoader载入的,右边的是由loader2载入的,因此,会出现ClassCastException
wingardium 2011-11-17
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 goldenfish1919 的回复:]
跑了一下,确实是ClassCastException。

loader1的父类加载器是是AppClassLoader
loader2的父类加载器是是ExtClassLoader

根据委托模型,loader1.loadClass委托给父类加载器AppClassLoader,eclipse下面的bin目录是在classpath里面的,因此,被AppClassLoader成功加载。

l……
[/Quote]

那请问从类Andromeda强转到类Andromeda, 会抛出异常呢?
qybao 2011-11-17
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 goldenfish1919 的回复:]
跑了一下,确实是ClassCastException。

loader1的父类加载器是是AppClassLoader
loader2的父类加载器是是ExtClassLoader

根据委托模型,loader1.loadClass委托给父类加载器AppClassLoader,eclipse下面的bin目录是在classpath里面的,因此,被AppClassLoader成功加载。

l……
[/Quote]
看错了,开头是import而不是package,如果是package,loadClass要带上package才能找到类
若鱼1919 2011-11-17
  • 打赏
  • 举报
回复

跑了一下,确实是ClassCastException。

loader1的父类加载器是是AppClassLoader
loader2的父类加载器是是ExtClassLoader

根据委托模型,loader1.loadClass委托给父类加载器AppClassLoader,eclipse下面的bin目录是在classpath里面的,因此,被AppClassLoader成功加载。

loader2.loadClass委托给父类加载器ExtClassLoader,但是加载不到,因此只能自己加载,会导致ClassCastException。


softroad 2011-11-17
  • 打赏
  • 举报
回复
getInstance方法是无参数的,不知道clazz1.getDeclaredMethod("getInstance", new Class[0]),这样会抛NoSuchMethodException吧。
qybao 2011-11-17
  • 打赏
  • 举报
回复
感觉应该会找不到类吧
因为类是有package的,而URL指定的路径是不会展开jar或子目录的,必须在指定路径下的class才是有效的,所以感觉可能会找不到类
龙四 2011-11-17
  • 打赏
  • 举报
回复
似乎会ClassCastException

62,614

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧