自定义classloader抛出java.lang.LinkageError异常

[color=red]自己实现了一个classLoader,代码如下[/color]

[color=red]类 SuC [/color]
package com.load;

public class SuC {
}

[color=red]接口 InterTest [/color]
package com.load;

public interface InterTest {

}

[color=red]类 TestIt[/color]
package com.load;

public class TestIt extends SuC implements InterTest {

}

[color=red]主类 MyClassLoader[/color]
[size=medium]package com.load;

import java.io.InputStream;
import java.lang.reflect.Method;

public class MyClassLoader extends ClassLoader{
public Class create() throws Exception{

             [color=green] //装载TestIt这个类[/color]      
              InputStream is = this.getClass().getClassLoader().getResourceAsStream("com/load/TestIt.class");
    int le = is.available();
    byte[] b = new byte[le];
    is.read(b, 0, le);
    Class cls = defineClass("com.load.TestIt",b,0,b.length);

              [color=green]//使用反射看看app加载器里是否装载[/color]      
              ClassLoader classLoader = getSystemClassLoader();
    Method method = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
    method.setAccessible(true);
    Object ccl = method.invoke(classLoader, "com.load.SuC");

              [color=green]//输出一下[/color]       System.out.println(ccl);
    System.out.println(((Class)ccl).getClassLoader());
    System.out.println(findLoadedClass("com.load.SuC").getClassLoader());

    [color=green]//装载TestIt的父类[/color]        InputStream iss = this.getClass().getClassLoader().getResourceAsStream("com/load/SuC.class");
    int lee = iss.available();
    byte[] bb = new byte[lee];
    iss.read(bb, 0, lee);
    Class clss = defineClass("com.load.SuC",bb,0,bb.length);

    return cls;
}
public static void main(String []args) throws Exception{
    MyClassLoader myCla = new MyClassLoader();
    myCla.create();
}

}[/size]

[size=medium][color=darkred] main方法中调用create方法,装载TestIt这个类,没有问题,利用反射输出一下,发现TestIt的父类SuC已经被系统类加载器加载。此时再去用defineClass加载SuC的字节流,则会抛出异常。[/color][/size]

[color=red]class com.load.SuC
sun.misc.Launcher$AppClassLoader@19821f
sun.misc.Launcher$AppClassLoader@19821f
Exception in thread "main" java.lang.LinkageError: loader (instance of com/load/MyClassLoader): attempted duplicate class definition for name: "com/load/SuC"
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(Unknown Source)
at java.lang.ClassLoader.defineClass(Unknown Source)
at com.load.MyClassLoader.create(MyClassLoader.java:30)
at com.load.MyClassLoader.main(MyClassLoader.java:45)[/color]

[color=red] 如果不加载SuC就没事。
或者SuC放在TestIt之前也没事,此时调整一下代码[/color][size=medium]package com.load;

import java.io.InputStream;
import java.lang.reflect.Method;

public class MyClassLoader extends ClassLoader{
public Class create() throws Exception{

    InputStream iss = this.getClass().getClassLoader().getResourceAsStream("com/load/SuC.class");
    int lee = iss.available();
    byte[] bb = new byte[lee];
    iss.read(bb, 0, lee);
    Class clss = defineClass("com.load.SuC",bb,0,bb.length);

    InputStream is = this.getClass().getClassLoader().getResourceAsStream("com/load/TestIt.class");
    int le = is.available();
    byte[] b = new byte[le];
    is.read(b, 0, le);
    Class cls = defineClass("com.load.TestIt",b,0,b.length);



    ClassLoader classLoader = getSystemClassLoader();
    Method method = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
    method.setAccessible(true);
    Object ccl = method.invoke(classLoader, "com.load.SuC");

    System.out.println(findLoadedClass("com.load.SuC").getClassLoader());
    System.out.println(ccl);
    System.out.println(((Class)ccl).getClassLoader());





    return cls;
}
public static void main(String []args) throws Exception{
    MyClassLoader myCla = new MyClassLoader();
    myCla.create();
}

}[/size]

[color=red]输出com.load.MyClassLoader@c17164
null
Exception in thread "main" java.lang.NullPointerException
at com.load.MyClassLoader.create(MyClassLoader.java:32)
at com.load.MyClassLoader.main(MyClassLoader.java:42)[/color]

[size=medium][color=darkred]我想问的是为什么SuC做为父类在TestIt被加载后,它自己被系统classloader加载。为何用在MyClassLoader里用defineClass再去加载字节流会出现java.lang.LinkageError的异常呢?MyClassLoader的实例应该没有加载过SuC。[/color][/size]

defineClass中调用的native方法中判断

http://java.sun.com/j2se/1.4.2/docs/api/java/lang/ClassLoader.html
默认的parent是SystemClassLoader

自定义classloader,你太猛了

classLoader加载一个类时,其父类也必然被加载(比如文中的SuC在defineClass("com.load.TestIt",b,0,b.length);返回前必然已经被加载,),注意不是之后,而是之前

父类的加载过程遵循一个规范,这个规范具体已经记不太清了,大致的流程应该是会调用当前的classloader的findClass加载(同样当前的类中用new加载的类亦如此);

文中定义的classloader过于简单了,默认的findClass实现仍然是调用父类的实现,所以会是系统classloader来加载。

至于异常,SystemClassloader加载过的类是不能被UserClassLoader加载的。