一、创建自定义类加载器
package com.example.jvm.classloader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; /** * Created by Think on 2019/6/9. */ public class MyTest16 extends ClassLoader{ private String classLoadName; private final String fileExtension = ".class"; public MyTest16(String classLoadName){ super(); //将系统类加载器当做该类加载器的父加载器 this.classLoadName = classLoadName; } public MyTest16(ClassLoader parent, String classLoadName){ super(parent); //显示指定该类加载器的父加载器器 this.classLoadName = classLoadName; } @Override public String toString() { return "[" + this.classLoadName + "]"; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { byte[] data = this.loadClassData(classLoadName); return this.defineClass(classLoadName,data, 0, data.length); } private byte[] loadClassData(String name){ InputStream is = null; byte[] data = null; ByteArrayOutputStream baos = null; try{ this.classLoadName = this.classLoadName.replace(".","//"); is = new FileInputStream(new File(name + 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; } public static void main(String[] args) throws Exception{ MyTest16 loader1 = new MyTest16("loader1"); test(loader1); } public static void test(ClassLoader classLoader) throws Exception { Class<?> clazz = classLoader.loadClass("com.example.jvm.classloader.MyTest1"); Object object = clazz.newInstance(); System.out.println(object); } }
打印结果
com.example.jvm.classloader.MyTest1@1540e19d
二、完善上一个实例创建的类加载器
命名空间:
每个类加载器都有自己的命名空间,命名空间由该加载器及所有父加载器所加载的类组成。
在同一个命名空间中,不会出现类的完整名字(包括类的包名)相同的两个类。
在不同的命名空间中,有可能会出现类的完整名字(包括类的包名)相同的两个类。
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("className11:" +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; } public static void main(String[] args) throws Exception{ MyTest16 loader1 = new MyTest16("loader1"); //loader1.setPath("D:/workspace/study/ jvm_demo/build/classes/java/main/"); loader1.setPath("D:/temp/"); // 将 D:/workspace/study/ jvm_demo/build/classes/java/main/com/example/jvm/classloader MyTest1文件移动到 D:/temp/com/example/jvm/classloader目录下 Class<?> clazz = loader1.loadClass("com.example.jvm.classloader.MyTest1"); // System.out.println("class:" + clazz.hashCode()); Object object = clazz.newInstance(); System.out.println(object); //System.out.println(object.getClass().getClassLoader()); MyTest16 loader2 = new MyTest16("loader2"); loader2.setPath("D:/temp/"); Class<?> clazz2 = loader2.loadClass("com.example.jvm.classloader.MyTest1"); // System.out.println("class2:" + clazz2.hashCode()); Object object2 = clazz2.newInstance(); System.out.println(object2); } }
将 D:/workspace/study/ jvm_demo/build/classes/java/main/com/example/jvm/classloader MyTest1文件移动到 D:/temp/com/example/jvm/classloader目录下
打印结果:
findClass invoked:com.example.jvm.classloader.MyTest1 class loader name: loader1 className11:loader1 class:21685669 com.example.jvm.classloader.MyTest1@7f31245a findClass invoked:com.example.jvm.classloader.MyTest1 class loader name: loader2 className11:loader2 class2:1173230247 com.example.jvm.classloader.MyTest1@330bedb4
1)loader1 和loader2 是两个实例,构成了两个不同的命名空间。
此时使用的是自定义类加载器。两个类的hascode值是不一样的。
2)如果将D:/workspace/study/ jvm_demo/build/classes/java/main/com/example/jvm/classloader MyTest1文件不移除,
打印的结果:
class:356573597 com.example.jvm.classloader.MyTest1@677327b6 class2:356573597 com.example.jvm.classloader.MyTest1@14ae5a5
两个列的hasCode值是一样的,是同一个值。使用的类加载器是APP类加载器。 因为测试,父加载器会加载D:/workspace/study/ jvm_demo/build/classes/java/main/com/example/jvm/classloader的MyTest1.class 文件,加载到了。自定义类加载器就不需要在加载了。
三、在二的基础上进行改造
package com.example.jvm.classloader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; /** * Created by Think on 2019/6/9. */ 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; } public static void main(String[] args) throws Exception{ MyTest16 loader1 = new MyTest16("loader1"); //loader1.setPath("D:/workspace/study/ jvm_demo/build/classes/java/main/"); loader1.setPath("D:/temp/"); //删除 将 D:/workspace/study/ jvm_demo/build/classes/java/main/com/example/jvm/classloader MyTest1文件移动到 D:/temp/com/example/jvm/classloader目录下 Class<?> clazz = loader1.loadClass("com.example.jvm.classloader.MyTest1"); // System.out.println("class:" + clazz.hashCode()); Object object = clazz.newInstance(); System.out.println(object); System.out.println(); MyTest16 loader2 = new MyTest16(loader1,"loader2"); loader2.setPath("D:/temp/"); Class<?> clazz2 = loader2.loadClass("com.example.jvm.classloader.MyTest1"); // System.out.println("class:" + clazz2.hashCode()); Object object2 = clazz2.newInstance(); System.out.println(object2); } }
删除工程里的MyTest1.class,保留D: emp 目录下的MyTest1.class 文件。
设置MyTest16 loader2 = new MyTest16(loader1,"loader2");
输出结果如下:
findClass invoked:com.example.jvm.classloader.MyTest1 class loader name: loader1 class:21685669 com.example.jvm.classloader.MyTest1@7f31245a class:21685669 com.example.jvm.classloader.MyTest1@6d6f6e28
hashCode值都是21685669
MyTest1由自定义loader1加载,
loader2委托loader1加载,loader1已经加载过了MyTest类,所以loader2不需要加载了。
四、在三的基础上进行改造
public static void main(String[] args) throws Exception{ MyTest16 loader1 = new MyTest16("loader1"); //loader1.setPath("D:/workspace/study/ jvm_demo/build/classes/java/main/"); loader1.setPath("D:/temp/"); Class<?> clazz = loader1.loadClass("com.example.jvm.classloader.MyTest1"); // System.out.println("class:" + clazz.hashCode()); Object object = clazz.newInstance(); System.out.println(object); System.out.println(); MyTest16 loader2 = new MyTest16(loader1,"loader2"); loader2.setPath("D:/temp/"); Class<?> clazz2 = loader2.loadClass("com.example.jvm.classloader.MyTest1"); // System.out.println("class:" + clazz2.hashCode()); Object object2 = clazz2.newInstance(); System.out.println(object2); System.out.println(); MyTest16 loader3 = new MyTest16(loader2,"loader3"); loader3.setPath("D:/temp/"); Class<?> clazz3 = loader3.loadClass("com.example.jvm.classloader.MyTest1"); // System.out.println("class:" + clazz3.hashCode()); Object object3 = clazz3.newInstance(); System.out.println(object3); System.out.println(); }
删除工程下的的MyTest1.class,保留D: emp 下的MyTest1.class文件, 打印结果
findClass invoked:com.example.jvm.classloader.MyTest1 class loader name: loader1 class:21685669 com.example.jvm.classloader.MyTest1@7f31245a class:21685669 com.example.jvm.classloader.MyTest1@6d6f6e28 class:21685669 com.example.jvm.classloader.MyTest1@135fbaa4