一、自定义类加载器在复杂类情况下的运行分析
1、使用之前创建的类加载器
public class MyTest16 extends ClassLoader{ private String className; //目录 private String path; private final String fileExtension = ".class"; public MyTest16(String classLoadName){ super(); //将系统类加载器当做该类加载器的父加载器 this.className = classLoadName; } public MyTest16(ClassLoader parent, String classLoadName){ super(parent); //显示指定该类加载器的父加载器器 this.className = classLoadName; } public void setPath(String path) { this.path = path; } @Override public String toString() { return "[" + this.className + "]"; } @Override protected Class<?> findClass(String clasName) throws ClassNotFoundException { System.out.println("findClass invoked:" + clasName); System.out.println("class loader name: " + this.className); byte[] data = this.loadClassData(clasName); return this.defineClass(clasName,data, 0, data.length); } private byte[] loadClassData(String className){ InputStream is = null; byte[] data = null; ByteArrayOutputStream baos = null; try{ className = className.replace(".","//"); //System.out.println("className:" +this.className); is = new FileInputStream(new File(this.path + className + this.fileExtension)); baos = new ByteArrayOutputStream(); int ch = 0; while ( -1 != (ch = is.read())){ baos.write(ch); } data = baos.toByteArray(); }catch (Exception ex){ ex.printStackTrace(); }finally { try { is.close(); baos.close(); }catch (Exception ex){ ex.printStackTrace(); } } return data; } }
2、创建MyCat类
public class MyCat { public MyCat(){ System.out.println("MyCat is loaded by:" + this.getClass().getClassLoader()); } }
3、创建MySample类
public class MySample { public MySample(){ System.out.println("MySample is loaded by:" + this.getClass().getClassLoader()); new MyCat(); } }
4、创建测试类
public class MyTest17 { public static void main(String[] args) throws Exception{ MyTest16 loader1 = new MyTest16("loader1"); Class<?> clazz = loader1.loadClass("com.example.jvm.classloader.MySample"); System.out.println("class:" + clazz.hashCode()); //如果注释掉改行,那么并不会实例化MySample对象,即MySample构造方法不会被调用 //因此不会实例化MyCat对象,即没有对MyCat进行主动使用,这里就不会加重MyCat Class Object object = clazz.newInstance(); } }
打印结果
class:1735600054 MySample is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2 MyCat is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
增加-XX:+TraceClassLoading后的打印结果
如果去掉Object object = clazz.newInstance();
打印结果为
说明:如果注释掉Object object = clazz.newInstance();该行,那么并不会实例化MySample对象,即MySample构造方法不会被调用
因此不会实例化MyCat对象,即没有对MyCat进行主动使用,这里就不会加载MyCat Class。
而且这个例子说明MyCat没有被预先加载
二、对一的Code进行改造
在一的基础上,新建一个测试类
public class MyTest17_1 { public static void main(String[] args) throws Exception{ MyTest16 loader1 = new MyTest16("loader1"); loader1.setPath("D:/temp/"); Class<?> clazz = loader1.loadClass("com.example.jvm.classloader.MySample"); System.out.println("class:" + clazz.hashCode()); //如果注释掉该行,那么并不会实例化MySample对象,即MySample构造方法不会被调用 //因此不会实例化MyCat对象,即没有对MyCat进行主动使用,这里就不会加载MyCat Class Object object = clazz.newInstance(); } }
里面增加了一个方法loader1.setPath("D:/temp/");
然后将MyCat.class 和MySample.class 剪切到D:/temp/目录下,如下面两图
打印结果:
findClass invoked:com.example.jvm.classloader.MySample class loader name: loader1 class:2133927002 MySample is loaded by:[loader1] findClass invoked:com.example.jvm.classloader.MyCat class loader name: loader1 MyCat is loaded by:[loader1]
三、类加载命名空间
在二的基础上,MyCat.java 增加一行代码System.out.println("from MyCat:" + MySample.class);
public class MyCat { public MyCat(){ System.out.println("MyCat is loaded by:" + this.getClass().getClassLoader()); System.out.println("from MyCat:" + MySample.class); } }
然后重新build,打印结果如下:
class:1735600054 MySample is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2 MyCat is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2 from MyCat:class com.example.jvm.classloader.MySample
现在将build路径下的classloader文件夹拷贝到D:\temp\com\example\jvm\classloader下,删除build路径下的MySample.class
打印输出结果:
findClass invoked:com.example.jvm.classloader.MySample class loader name: loader1 class:2133927002 MySample is loaded by:[loader1] MyCat is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2 Exception in thread "main" java.lang.NoClassDefFoundError: com/example/jvm/classloader/MySample at com.example.jvm.classloader.MyCat.<init>(MyCat.java:10) at com.example.jvm.classloader.MySample.<init>(MySample.java:10) at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:423) at java.lang.Class.newInstance(Class.java:442) at com.example.jvm.classloader.MyTest17_1.main(MyTest17_1.java:16) Caused by: java.lang.ClassNotFoundException: com.example.jvm.classloader.MySample at java.net.URLClassLoader.findClass(URLClassLoader.java:381) at java.lang.ClassLoader.loadClass(ClassLoader.java:424) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335) at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ... 8 more
说明:MySample由自定义loader加载,MyCat由AppClassLoader。两个类是不同的loader加载,处于不同的命名空间。
所以System.out.println("from MyCat:" + MySample.class); 抛出异常。 子加载器所加载的类能够访问父加载器所加载的类。反之,父加载器所加载的类无法访问子加载器所加载的类
作者:Work Hard Work Smart
出处:http://www.cnblogs.com/linlf03/
欢迎任何形式的转载,未经作者同意,请保留此段声明!