一、创建自定义类加载器
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