黑马程序员技术交流社区

标题: 自定义类加载器的一个小问题,请大师帮忙 [打印本页]

作者: 贾浩田    时间: 2014-9-30 14:51
标题: 自定义类加载器的一个小问题,请大师帮忙
  1. package Study.ClassLoader;

  2. import java.io.ByteArrayOutputStream;
  3. import java.io.File;
  4. import java.io.FileInputStream;
  5. import java.io.IOException;

  6. public class Test2 {
  7.         public static void main(String[] args) {
  8.                 // TODO Auto-generated method stub
  9.                 MineClassLoader loader = new MineClassLoader("myClassLoader");   //创建一个类加载器并自定义一个名称
  10.                 try {
  11.                         Class<?> clazz = loader.loadClass("Animal.class");    //类加载器的作用:通过类名,将硬盘中的字节码文件加载进内存并创建字节码对象
  12.                         try {
  13.                                 Object obj = clazz.newInstance();         //所以类加载器最终要创建对象
  14.                                 Animal animal = (Animal)obj;
  15.                                 animal.println();                  //看打印结果
  16.                                
  17.                                 //System.out.println(Animal.getClass().getClassLoader().getClass().getName());
  18.                                
  19.                         } catch (InstantiationException | IllegalAccessException e) {
  20.                                 // TODO Auto-generated catch block
  21.                                 e.printStackTrace();
  22.                         }
  23.                 } catch (ClassNotFoundException e) {
  24.                         // TODO Auto-generated catch block
  25.                         e.printStackTrace();
  26.                 }

  27.         }

  28. }
  29. class Animal{
  30.         void println(){
  31.                 System.out.println("一个自定义类");
  32.         }
  33. }
  34. class MineClassLoader extends ClassLoader{
  35.         private String name;        //定义成员,类加载器的名称
  36.        
  37.         private String class_path = "E:\\黑马训练营\\ITHEIMA\\高新技术\\bin\\Study\\ClassLoader\\";   //把要加载的类的绝对路径存储起来
  38.         //E:\黑马训练营\ITHEIMA\高新技术\bin\Study\ClassLoader\Animal.class
  39.         MineClassLoader(String name){      //给自定义类加载器定义一个构造器
  40.                 this.name = name;
  41.         }
  42.         MineClassLoader(ClassLoader loader, String name){
  43.                 super(loader);           //调用父类的构造器:使用指定的类加载器来创建新的类加载器
  44.                 this.name = name;
  45.         }
  46.         //注意,覆盖父类的方法可以通过IDE自动生成,通过类名来获取该类的字节码对象,这个findClass方法被loadClass方法隐式调用
  47.         @Override  //这个方法充分的体现了类加载器的作用
  48.         protected Class<?> findClass(String name) throws ClassNotFoundException {   //不能抛出比父类级别高的异常
  49.                 // TODO Auto-generated method stub
  50.                 String path = this.class_path + name;         //要加载的字节码文件的绝对路径
  51.                 System.out.println(path);   //打印测试一下
  52.                 try {
  53.                         FileInputStream fi = new FileInputStream(new File(path));     //把要加载的字节码文件关联字节读取流
  54.                         ByteArrayOutputStream ba = new ByteArrayOutputStream();       //创建一个内存输出流
  55.                         int num = 0 ;
  56.                         //读取这份字节码文件
  57.                         while((num = fi.read()) != -1){  
  58.                                 ba.write(num);        //写入内存流
  59.                                 ba.flush();
  60.                         }
  61.                         fi.close();    //关闭读取流
  62.                         byte[] buf = ba.toByteArray();   //将内存流中的字节转换成字节数组
  63.                         ba.close();    //关闭内存流
  64.                         //返回一份字节码
  65.                         return this.defineClass(this.name, buf, 0, buf.length);
  66.                                        
  67.                 } catch (IOException e) {
  68.                         // TODO Auto-generated catch block
  69.                         e.printStackTrace();
  70.                 }               
  71.                 //使用原类加载器加载
  72.                 return super.findClass(path);
  73.         }               
  74. }
复制代码
E:\黑马训练营\ITHEIMA\高新技术\bin\Study\ClassLoader\Animal.class
Exception in thread "main" java.lang.NoClassDefFoundError: myClassLoader (wrong name: Study/ClassLoader/Animal)
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(Unknown Source)
    at java.lang.ClassLoader.defineClass(Unknown Source)
    at Study.ClassLoader.MineClassLoader.findClass(Test2.java:74)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at Study.ClassLoader.Test2.main(Test2.java:18)

总是报告我.  NoClassDefFoundError: myClassLoader (wrong name: Study/ClassLoader/Animal)异常,求解释

作者: 水竹    时间: 2014-9-30 16:02
13行改成试试:Class<?> clazz = loader.loadClass("Animal");
作者: 贾浩田    时间: 2014-9-30 16:19
水竹 发表于 2014-9-30 16:02
13行改成试试:Class clazz = loader.loadClass("Animal");

不管用,这个异常NoClassDefFoundError是一个Error,属于严重异常,意思是说:连接时从内存找不到需要的class就出现NoClassDefFoundError,但是我不理解为什么?
作者: 水竹    时间: 2014-9-30 16:23
贾浩田 发表于 2014-9-30 16:19
不管用,这个异常NoClassDefFoundError是一个Error,属于严重异常,意思是说:连接时从内存找不到需要的c ...

那肯定就是你目录、包名之类的问题,因为我没有E盘,所以为了省事直接把所有有关包的语句都去掉了,然后在当前目录直接生成的类文件,最后改了我上面说的那里,成功运行。
作者: 水竹    时间: 2014-9-30 16:30
下面代码中我改了三处地方,如我所说,成功运行,所以肯定是你包名、目录之类的问题。

  1. // 改了,把包去掉了
  2. //package Study.ClassLoader;

  3. import java.io.ByteArrayOutputStream;
  4. import java.io.File;
  5. import java.io.FileInputStream;
  6. import java.io.IOException;

  7. public class Test2{
  8.         public static void main(String[] args) {
  9.                 // TODO Auto-generated method stub
  10.                 MineClassLoader loader = new MineClassLoader("myClassLoader");   //创建一个类加载器并自定义一个名称
  11.                 try {
  12. // 改了,把.class去掉了
  13.                         Class<?> clazz = loader.loadClass("Animal");    //类加载器的作用:通过类名,将硬盘中的字节码文件加载进内存并创建字节码对象
  14.                         try {
  15.                                 Object obj = clazz.newInstance();         //所以类加载器最终要创建对象
  16.                                 Animal animal = (Animal)obj;
  17.                                 animal.println();                  //看打印结果
  18.                                 
  19.                                 //System.out.println(Animal.getClass().getClassLoader().getClass().getName());
  20.                                 
  21.                         } catch (InstantiationException | IllegalAccessException e) {
  22.                                 // TODO Auto-generated catch block
  23.                                 e.printStackTrace();
  24.                         }
  25.                 } catch (ClassNotFoundException e) {
  26.                         // TODO Auto-generated catch block
  27.                         e.printStackTrace();
  28.                 }

  29.         }

  30. }
  31. class Animal{
  32.         void println(){
  33.                 System.out.println("一个自定义类");
  34.         }
  35. }
  36. class MineClassLoader extends ClassLoader{
  37.         private String name;        //定义成员,类加载器的名称
  38.         
  39.         private String class_path = "E:\\黑马训练营\\ITHEIMA\\高新技术\\bin\\Study\\ClassLoader\\";   //把要加载的类的绝对路径存储起来
  40.         //E:\黑马训练营\ITHEIMA\高新技术\bin\Study\ClassLoader\Animal.class
  41.         MineClassLoader(String name){      //给自定义类加载器定义一个构造器
  42.                 this.name = name;
  43.         }
  44.         MineClassLoader(ClassLoader loader, String name){
  45.                 super(loader);           //调用父类的构造器:使用指定的类加载器来创建新的类加载器
  46.                 this.name = name;
  47.         }
  48.         //注意,覆盖父类的方法可以通过IDE自动生成,通过类名来获取该类的字节码对象,这个findClass方法被loadClass方法隐式调用
  49.         @Override  //这个方法充分的体现了类加载器的作用
  50.         protected Class<?> findClass(String name) throws ClassNotFoundException {   //不能抛出比父类级别高的异常
  51.                 // TODO Auto-generated method stub
  52. // 改了,把绝对路径中目录去掉了
  53.                 String path =name;         //要加载的字节码文件的绝对路径
  54.                 System.out.println(path);   //打印测试一下
  55.                 try {
  56.                         FileInputStream fi = new FileInputStream(new File(path));     //把要加载的字节码文件关联字节读取流
  57.                         ByteArrayOutputStream ba = new ByteArrayOutputStream();       //创建一个内存输出流
  58.                         int num = 0 ;
  59.                         //读取这份字节码文件
  60.                         while((num = fi.read()) != -1){  
  61.                                 ba.write(num);        //写入内存流
  62.                                 ba.flush();
  63.                         }
  64.                         fi.close();    //关闭读取流
  65.                         byte[] buf = ba.toByteArray();   //将内存流中的字节转换成字节数组
  66.                         ba.close();    //关闭内存流
  67.                         //返回一份字节码
  68.                         return this.defineClass(this.name, buf, 0, buf.length);
  69.                                        
  70.                 } catch (IOException e) {
  71.                         // TODO Auto-generated catch block
  72.                         e.printStackTrace();
  73.                 }               
  74.                 //使用原类加载器加载
  75.                 return super.findClass(path);
  76.         }               
  77. }
复制代码

作者: 贾浩田    时间: 2014-9-30 16:42
水竹 发表于 2014-9-30 16:23
那肯定就是你目录、包名之类的问题,因为我没有E盘,所以为了省事直接把所有有关包的语句都去掉了,然后 ...

我能问一下为什么啊? 开发中肯定不能去掉包名和项目路径名啊
作者: 水竹    时间: 2014-9-30 17:44
贾浩田 发表于 2014-9-30 16:42
我能问一下为什么啊? 开发中肯定不能去掉包名和项目路径名啊

抱歉,玩不转了。
本来以为包名和路径加上就好了,但是弄了半天也没弄出来。
作者: 贾浩田    时间: 2014-9-30 18:58
水竹 发表于 2014-9-30 17:44
抱歉,玩不转了。
本来以为包名和路径加上就好了,但是弄了半天也没弄出来。 ...

谢谢:),你的解决办法虽然没有帮我解决异常的问题,但是却解决了loadClass时,无法调用findClass方法




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2