11、类加载器的命名空间
11.1、概念
- 每个类加载器都有各自的命名空间,命名空间由该加载器及所有父加载器所加载的类组成。
- 在同一个命名空间中,不会出现全类名相同的两个类。
- 在不同的命名空间中,有可能出现全类名相同的两个类。
11.2、不同类加载器的命名空间关系
- 同一个命名空间中的类是相互可见的。
- 子加载器的命名空间包含了所有父加载器的命名空间,因此,子加载器中加载的类能看到所有父加载器中加载的类,而父加载器加载的类看不到子加载器加载的类。
- 如果两个加载器之间没有直接或间接的父子关系,那么它们各自加载的类互不可见。
11.3、案例分析
自定义两个类MySample和MyCat,在MySample类中使用MyCat类,在MyCat类中使用MySample类,
如果二者在“同一个命名空间”下,则相互访问没有问题。
如果一个在子命名空间,一个在父命名空间,则在子命名空间的类可以访问到父命名空间的类,而父命名空间中的类访问不到子命名空间的类(这种情况会报错java.lang.ClassNotFoundException)。
如果二者在两个没有任何父子关系的同级命名空间中,则不能相互访问。
package com.shtec.classLoader; /** * 删除类路径下的MySample.class,使用自定义类加载器加载MySample类; * MyCat类依旧使用【系统类加载器】加载, * 由于在系统类加载器的命名空间中访问不到自定义类加载器中命名空间加载的类,因此,在MyCat中使用MySample类会报异常: * java.lang.ClassNotFoundException: com.shtec.classLoader.MySample * @author sunhao * */ public class MyTest { public static void main(String[] args) throws Exception { CustomClassLoader myLoader = new CustomClassLoader("myLoader"); myLoader.setClassPath("D:\\classes\\"); Class<?> clazz = myLoader.loadClass("com.shtec.classLoader.MySample"); clazz.newInstance(); //输出: /* * MySample被com.shtec.classLoader.CustomClassLoader@6d06d69c加载 MyCat被sun.misc.Launcher$AppClassLoader@73d16e93加载 Exception in thread "main" java.lang.NoClassDefFoundError: com/shtec/classLoader/MySample at com.shtec.classLoader.MyCat.<init>(MyCat.java:7) at com.shtec.classLoader.MySample.<init>(MySample.java:6) at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source) at java.lang.reflect.Constructor.newInstance(Unknown Source) at java.lang.Class.newInstance(Unknown Source) at com.shtec.classLoader.MyTest.main(MyTest.java:11) Caused by: java.lang.ClassNotFoundException: com.shtec.classLoader.MySample at java.net.URLClassLoader.findClass(Unknown Source) at java.lang.ClassLoader.loadClass(Unknown Source) at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source) at java.lang.ClassLoader.loadClass(Unknown Source) ... 8 more */ } }
package com.shtec.classLoader; public class MySample { public MySample(){ System.out.println("MySample被" + this.getClass().getClassLoader() + "加载"); new MyCat();//MySample类中使用MyCat类 } }
package com.shtec.classLoader; public class MyCat { public MyCat(){ System.out.println("MyCat被" + this.getClass().getClassLoader() + "加载"); System.out.println(MySample.class);//MyCat类中使用MySample类 } }
package com.shtec.classLoader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; /** * 自定义类加载器的3个步骤: * 1、继承ClassLoader * 2、重写findClass()方法 * 3、在findClass()方法调用defineClass()方法 * * @author sunhao * */ public class CustomClassLoader extends ClassLoader{ private String classLoaderName; private String classPath; //类加载器的加载路径 private static final String suffix = ".class"; public CustomClassLoader(){} public CustomClassLoader(String classLoaderName){ super();//将系统类加载器当做该类加载器的父加载器 this.classLoaderName = classLoaderName; } public CustomClassLoader(ClassLoader parent, String classLoaderName){ super(parent);//显示指定该类加载器的父加载器 this.classLoaderName = classLoaderName; } public void setClassPath(String classPath) { this.classPath = classPath; } /* * *********************************************************************** * ClassLoader中的loadClass(String name, boolean resolve)方法会调用findClass方法* * 该方法执行,说明自定义类加载器生效 * * *********************************************************************** */ @Override protected Class<?> findClass(String name) throws ClassNotFoundException { //System.out.println("自定义类加载器【" + this.classLoaderName + "】查找类" + name); byte[] data = this.loadClassData(name); //defineClass方法将字节数组转化为Class对象 return this.defineClass(name, data, 0, data.length); } /* * 加载.class文件,并转换为字节数组 */ private byte[] loadClassData(String className){ InputStream in = null; byte[] data = null; ByteArrayOutputStream baos = null; className = className.replace(".", "/"); try { in = new FileInputStream(new File(this.classPath + className + suffix)); baos = new ByteArrayOutputStream(); byte[] buffer = new byte[8192]; int len = 0; while((len=in.read(buffer)) != -1){ baos.write(buffer, 0, len); } data = baos.toByteArray(); } catch (Exception e) { e.printStackTrace(); }finally{ if(in != null){ try { in.close(); } catch (IOException e) { e.printStackTrace(); } } if(baos != null){ try { baos.close(); } catch (IOException e) { e.printStackTrace(); } } } return data; } }