1、建立自定義類加載器java
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); } }
打印結果jvm
com.example.jvm.classloader.MyTest1@1540e19d
2、完善上一個實例建立的類加載器ide
命名空間:測試
每一個類加載器都有本身的命名空間,命名空間由該加載器及全部父加載器所加載的類組成。ui
在同一個命名空間中,不會出現類的完整名字(包括類的包名)相同的兩個類。this
在不一樣的命名空間中,有可能會出現類的完整名字(包括類的包名)相同的兩個類。spa
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目錄下code
打印結果:blog
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 是兩個實例,構成了兩個不一樣的命名空間。ssl
此時使用的是自定義類加載器。兩個類的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 文件,加載到了。自定義類加載器就不須要在加載了。
3、在二的基礎上進行改造
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:\temp 目錄下的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不須要加載了。
4、在三的基礎上進行改造
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:\temp 下的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