ClassLoader 问题,请Java的大内高手帮帮忙!!!
public void testLoaderDiffJar() throws ClassNotFoundException, MalformedURLException, InstantiationException, IllegalAccessException {
ClassA orgA = new ClassA();
orgA.print();
File classesDir = new File( "C:\\ClassA.jar ");
ClassLoader parentLoader = ClassA.class.getClassLoader();
URLClassLoader loader1 = new URLClassLoader(
new URL[] { classesDir.toURL() }, parentLoader);
Class cls1 = loader1.loadClass( "com.my.test.loader.ClassA ");
ClassA a = (ClassA) cls1.newInstance();
a.print();
}
----------------
问题:ClassLoader上来的类并没有替换原先的类。a.print();出来的信息还是旧的,请问这是为什么?
[解决办法]
你没有明白 ClassLoader的 运行机制
ClassLoader在加载类的时候回首先把请求委托给它的父loader(也就是 你代码中的parentLoader ) 去加载
所以你的URLClassLoader在加载 ClassA的时候 会调用自己的父Loader 去加载
而这是 ClassA 已经被记载了,因为你前面的代码有ClassA 的操作
所以就出现了这种情况
要达到你的目的,要将 URLClassLoader 的机制改写一下 不让它委托加载
在defineClass(方法名我记不清出了)里面可以改写 你自己试试
不明白再交流
[解决办法]
/*
JDK版本:jdk1.4.2与jdk1.6.0
目录文件位置:
F:\csdn\20070129\ClassA.class
F:\csdn\20070129\ClassA.java
F:\csdn\20070129\ClassA.jar
F:\csdn\20070129\my\ClassA.class
F:\csdn\20070129\my\ClassA.java
F:\csdn\20070129\ClassA.jar文件内容:
my\ClassA.class
my\ClassA.java
编译: F:\csdn\20070129> javac ClassA.java my\ClassA.java
打包: F:\csdn\20070129> jar -cvf ClassA.jar my
运行: F:\csdn\20070129> java ClassA
结果: 请猜一下????????能用双亲委派的原理分析出正确结果吗?????????
最后: 试试在getClassLoader()后加“.getParent()”看看结果如何?????????
试试在ClassA前加“my.”看看结果又是如何?????????
*/
//F:\csdn\20070129\my\ClassA.java源码
package my;
public class ClassA{
public void print() {
System.out.println( "my HACK ClassA ");
}
}
//F:\csdn\20070129\ClassA.java源码
import java.net.*;
import java.io.*;
public class ClassA extends ClassLoader {
public void print() {
System.out.println( "my ClassA ");
}
public static void main(String[] args) {
System.out.println( "第一种方式: ");
System.out.println( "----------------------------------- ");
ClassA orgA = new ClassA();
orgA.print();
System.out.println( "ClassLoader name: "+ClassA.class.getClassLoader());
File classesDir = new File( "ClassA.jar ");
try {
System.out.println( "\r\n第二种方式: ");
System.out.println( "----------------------------------- ");
ClassLoader parentLoader = ClassA.class.getClassLoader();
URLClassLoader loader = new URLClassLoader(
new URL[] { classesDir.toURL() }, parentLoader);
Class c = loader.loadClass( "my.ClassA ");
System.out.println( "ClassLoader name: "+c.getClassLoader());
ClassA a = (ClassA) c.newInstance();
a.print();
} catch (Throwable e) {
e.printStackTrace();
}
try {
System.out.println( "\r\n第三种方式: ");
System.out.println( "----------------------------------- ");
Class c = new ClassA().findClass( "my.ClassA ");
System.out.println( "ClassLoader name: "+c.getClassLoader());
my.ClassA a = (my.ClassA) c.newInstance();
a.print();
} catch (Throwable e) {
e.printStackTrace();
}
try {
System.out.println( "\r\n第四种方式: ");
System.out.println( "----------------------------------- ");
URLClassLoader loader = new URLClassLoader(
new URL[] { classesDir.toURL() }, new ClassA());
Class c = loader.loadClass( "my.ClassA ");
System.out.println( "ClassLoader name: "+c.getClassLoader());
my.ClassA a = (my.ClassA) c.newInstance();
a.print();
} catch (Throwable e) {
e.printStackTrace();
}
}
protected Class findClass(String className) throws ClassNotFoundException {
byte classData[]=null;
try {
String fileName=className.replace( '. ',File.separatorChar)+ ".class ";
System.out.println( "fileName: "+fileName);
FileInputStream fis = new FileInputStream(fileName);
BufferedInputStream bis = new BufferedInputStream(fis);
ByteArrayOutputStream out = new ByteArrayOutputStream();
int c = bis.read();
while (c != -1) {
out.write(c);
c = bis.read();
}
classData=out.toByteArray();
fis.close();bis.close();out.close();
} catch (Exception e) {
e.printStackTrace();
throw new ClassNotFoundException();
}
return defineClass(className, classData, 0, classData.length);
}
}
[解决办法]
KRplusSRequalGOD(狂人+善人=神) 说得对。
如果
Class cls1 = loader1.loadClass( "com.my.test.loader.ClassA ");
载入的真是 "C:\ClassA.jar " 里的那个 class
ClassA a = (ClassA) cls1.newInstance();
就会发生 ClassCastException 了,因为它们虽然名字一样,却不是同一个东西。
[解决办法]
上面的解释很全面啊,那我就说说楼主的代码怎么改吧(有骗分嫌疑^_^):
ClassLoader parentLoader = ClassA.class.getClassLoader();
URLClassLoader loader1 = new URLClassLoader(
new URL[] { classesDir.toURL() }, parentLoader);
首先:parentLoader不用设,直接给个null强制让自己加载,而不是通过父loader加载。
Class cls1 = loader1.loadClass( "com.my.test.loader.ClassA ");
ClassA a = (ClassA) cls1.newInstance();
不能强制转换,因为转换的类不是你从URL中加载的类。所以这两行代码可以改为:
Method method = cls1.getMethod( "print ", null);
method.invoke(this, null); //this换成你自己的对象就可以了。
[解决办法]
借宝地保存一下测试代码,以后遇到类似问题方便查看:
Test.java
package test;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
public class Test {
public static void main(String[] args) throws Exception {
ClassA.print();
File file = new File( "C:/ ");
URL url = file.toURL();
URLClassLoader loader1 = new URLClassLoader(new URL[] { url }, null);
Class cls1 = loader1.loadClass( "ClassA ");
Method method = cls1.getMethod( "print ", null);
method.invoke(cls1.newInstance(), null);
}
}
ClassA.java(有包)
package test;
public class ClassA {
public static void print() {
System.out.println( "This class is ClassA with pakage test. ");
}
}
ClassA.java(无包)
public class ClassA {
public static void print() {
System.out.println( "This class is ClassA that hasn 't pakage. ");
}
}
输出:
This class is ClassA with pakage test.
This class is ClassA that hasn 't pakage.