自定义classloader问题。

VinCiOo 2012-10-18 02:06:17
MyclassLoader.java
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;

public class MyClassLoader extends ClassLoader{
private String filePath;
public MyClassLoader(String filePath){
this.filePath=filePath;
}

protected Class<?> findClass(String className)throws ClassNotFoundException{
Class clazz=this.findLoadedClass(className);
if(null==clazz){//为什么不写clazz==null
try{
byte[] bytes=loadClassBytes(className);
//定义clazz类
clazz=defineClass(className,bytes,0,bytes.length);
}catch(IOException e){
throw new ClassNotFoundException(e.toString());
}
}
return clazz;
}

private byte[] loadClassBytes(String className)throws IOException{
//获取class文件路径
String classFile=getClassFile(className);
System.out.println(classFile);

FileInputStream fis=null;
try {
fis = new FileInputStream(classFile);
} catch (FileNotFoundException e) {
System.out.println(e);
return null;
}
byte[] bytes = new byte[fis.available()];
fis.read(bytes);
fis.close();
return bytes;

}

//将文件路径与文件名组合
private String getClassFile(String name){
StringBuffer sb=new StringBuffer(filePath);
name=name.replace('.',File.separatorChar)+".class";
sb.append(File.separatorChar+name);
return sb.toString();
}

}

MainTest.java
public class MainTest {
public static void main(String[] args) {
try {
MyClassLoader cl = new MyClassLoader("E:\\eclipse\\workspace\\ClassLoader\\bin");
Class c = cl.loadClass("com.Hello");
System.out.println(c.getClassLoader());
Hello hello = (Hello) c.newInstance();
hello.say();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
Hello.java就是我写的一个简单的HelloWorld类,里面的方法say(),输出“hello!”。
Hello.java
public class Hello {
//public static void main(String[] args){
public void say(){
System.out.println("hello!");
}
}
问题出在蓝色字体那一句,如果调用loadClass方法,那么根本就没有运行我自定义的findClass(),而是通过系统AppClassLoader就成功加载了类。那么如果把这句换成Class c = cl.findClass("com.Hello");那么,直接运行我自定义的findClass()方法,但是在MainTest类中Hello hello = (Hello) c.newInstance();这一句会出错,不能转换成Hello类,怎么回事?
...全文
260 22 打赏 收藏 转发到动态 举报
写回复
用AI写文章
22 条回复
切换为时间正序
请发表友善的回复…
发表回复
dracularking 2012-10-19
  • 打赏
  • 举报
回复
谢谢,这jvms7好像挺新的

确实有一段:
We will sometimes represent a class or interface using the notation <N, Ld>, where
N denotes the name of the class or interface and Ld denotes the defining loader of
the class or interface.

再慢慢看。
龙四 2012-10-18
  • 打赏
  • 举报
回复
下载地址:http://www.iteye.com/topic/1117824

[Quote=引用 19 楼 的回复:]

想详细了解一下,找了一下,没有很轻易找到。
[/Quote]
龙四 2012-10-18
  • 打赏
  • 举报
回复
《java虚拟机规范 JAVASE 7 版》第五章

[Quote=引用 18 楼 的回复:]

哦是这样的策略啊,加载器只管它祖先类的加载内容以避免可能的重复加载,其它非相关加载器即使加载了完全相同的类,它也联系不到?谢谢了,回答的很详细,不过这些有没有官方文档的出处啊?
[/Quote]
dracularking 2012-10-18
  • 打赏
  • 举报
回复
想详细了解一下,找了一下,没有很轻易找到。
dracularking 2012-10-18
  • 打赏
  • 举报
回复
哦是这样的策略啊,加载器只管它祖先类的加载内容以避免可能的重复加载,其它非相关加载器即使加载了完全相同的类,它也联系不到?谢谢了,回答的很详细,不过这些有没有官方文档的出处啊?
龙四 2012-10-18
  • 打赏
  • 举报
回复
jvm中区分一个类并不是单单一个类的全限定名,还包括是装载它的classLoader,两者一起唯一确定一个Class

每个加载器都有一个命名空间,所谓的命名空间就是加载器维护了一张表,表的内容为其作为初始类装载器所加载的类,也就是说,如加载器L,不管类是不是由L装载进jvm的,只要是L的父类加载器路径中的某个加载器装载的,L都会将这个装载的类记录下来,下次再使用L加载同样的类时,就会返回这个已经装载过的类。如果没有就会去加载。也就是说,同一个class文件,在jvm中可能存在多个对应的Class对象


在做强制转换的时候,会判断引用类型的Class与对象所属类的Class是不是同一个或者有没有父子关系

[Quote=引用 16 楼 的回复:]

引用 4 楼 的回复:

Hello hello = (Hello) c.newInstance();这一句会出错

你看看Hello.class.getClassLoader与c.getClassLoader是不是同一个,如果不是,在逻辑上它们就不是同一个类,就是ClassCastException

因为Hello是通过defineClass生成的,类完全相同的话还不能决定是两……
[/Quote]
dracularking 2012-10-18
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 的回复:]

Hello hello = (Hello) c.newInstance();这一句会出错

你看看Hello.class.getClassLoader与c.getClassLoader是不是同一个,如果不是,在逻辑上它们就不是同一个类,就是ClassCastException
[/Quote]
因为Hello是通过defineClass生成的,类完全相同的话还不能决定是两个相同的类?
VinCiOo 2012-10-18
  • 打赏
  • 举报
回复
[Quote=引用 14 楼 的回复:]
反射我也有篇博客:
http://www.ticmy.com/?p=200
[/Quote]
小女子万分感谢!
龙四 2012-10-18
  • 打赏
  • 举报
回复
反射我也有篇博客:
http://www.ticmy.com/?p=200

[Quote=引用 13 楼 的回复:]

引用 12 楼 的回复:
newInstance的时候就会调用构造方法,所以你放到构造方法中就会执行

反射
Object hello = c.newInstance();
Method m = c.getMethod("say");
m.invoke(hello);


多谢啊!我还没用过反射,我得赶紧看一看,挺好用的。
[/Quote]
VinCiOo 2012-10-18
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 的回复:]
newInstance的时候就会调用构造方法,所以你放到构造方法中就会执行

反射
Object hello = c.newInstance();
Method m = c.getMethod("say");
m.invoke(hello);
[/Quote]

多谢啊!我还没用过反射,我得赶紧看一看,挺好用的。
龙四 2012-10-18
  • 打赏
  • 举报
回复
newInstance的时候就会调用构造方法,所以你放到构造方法中就会执行

反射
Object hello = c.newInstance();
Method m = c.getMethod("say");
m.invoke(hello);

[Quote=引用 11 楼 的回复:]

引用 10 楼 的回复:
通过反射


谢谢你哦,问题解决了,我最后把Hello.java改成了:
public class Hello {
//public static void main(String[] args){
public Hello(){
System.out.println("hello!");
}
}

在MainTest.java中直接用Obje……
[/Quote]
VinCiOo 2012-10-18
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 的回复:]
通过反射
[/Quote]

谢谢你哦,问题解决了,我最后把Hello.java改成了:
public class Hello {
//public static void main(String[] args){
public Hello(){
System.out.println("hello!");
}
}

在MainTest.java中直接用Object o=c.newInstance(); 就可以了运行了。

另外,通过反射具体是怎么弄呢,愿闻其详。
龙四 2012-10-18
  • 打赏
  • 举报
回复
通过反射

[Quote=引用 9 楼 的回复:]

引用 4 楼 的回复:
Hello hello = (Hello) c.newInstance();这一句会出错

你看看Hello.class.getClassLoader与c.getClassLoader是不是同一个,如果不是,在逻辑上它们就不是同一个类,就是ClassCastException


我这样写是有问题,那么请问该怎么写呢,怎么写才能运行Hello类中的say()……
[/Quote]
VinCiOo 2012-10-18
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 的回复:]
Hello hello = (Hello) c.newInstance();这一句会出错

你看看Hello.class.getClassLoader与c.getClassLoader是不是同一个,如果不是,在逻辑上它们就不是同一个类,就是ClassCastException
[/Quote]

我这样写是有问题,那么请问该怎么写呢,怎么写才能运行Hello类中的say()方法?
龙四 2012-10-18
  • 打赏
  • 举报
回复
这要把class文件放到指定的目录下的,这些类只能存在于自己定义的classloader的classpath里且不能存在于父classloader的classpath里,也就是让父加载器找不到指定的类,这样才会让自定义的类加载器去加载类

[Quote=引用 7 楼 的回复:]

引用 1 楼 的回复:
public static final void loadJarFile(File file) {
try {
addURL.invoke(system, new Object[] { file.toURI().toURL() });
// System.out.println("load_jar_package:" + file.getAbsolutePath……
[/Quote]
VinCiOo 2012-10-18
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 的回复:]
public static final void loadJarFile(File file) {
try {
addURL.invoke(system, new Object[] { file.toURI().toURL() });
// System.out.println("load_jar_package:" + file.getAbsolutePath())……
[/Quote]

不明白,我看网上很多例子都是这样写的。比如http://blog.csdn.net/liuxiaochen123/article/details/8031510这篇文章,但是他最后明明都没有用到自己写的findClass。
VinCiOo 2012-10-18
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 的回复:]
看看你自定义的classloader的getParent()返回的是什么,如果父加载器可以装在指定的类,就不会通过你自定义的classloader去装在了
[/Quote]

嗯,所以我直接调用重写的findClass()方法,用这句Class c = cl.findClass("com.Hello");会出现上述错误。类型不能转换。
VinCiOo 2012-10-18
  • 打赏
  • 举报
回复
你好,委托模型我看过了,难道我说的不对么?使用findclass调用的是它父类的AppClassLoader,而不是我自定义的,这句话不对么?[Quote=引用 2 楼 的回复:]
看看classloader的委托模型:http://www.ticmy.com/?p=257
[/Quote]
龙四 2012-10-18
  • 打赏
  • 举报
回复
Hello hello = (Hello) c.newInstance();这一句会出错

你看看Hello.class.getClassLoader与c.getClassLoader是不是同一个,如果不是,在逻辑上它们就不是同一个类,就是ClassCastException
龙四 2012-10-18
  • 打赏
  • 举报
回复
看看你自定义的classloader的getParent()返回的是什么,如果父加载器可以装在指定的类,就不会通过你自定义的classloader去装在了
加载更多回复(2)

51,410

社区成员

发帖
与我相关
我的任务
社区描述
Java相关技术讨论
javaspring bootspring cloud 技术论坛(原bbs)
社区管理员
  • Java相关社区
  • 小虚竹
  • 谙忆
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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