一、方法概述
阐述的方法loadClass、findClass、defineClass。
二、方法阐述
loadClass
loadClass在ClassLoader中有两个重载的方法,如下:
//第一处
Class<?> loadClass(String name) throws ClassNotFoundException
//第二处
Class<?> loadClass(String name, boolean resolve)
第一处
该方法只有一个参数,参数的含义就是欲加载的类的二进制名字,该方法的含义就是加载指定二进制名字的类,这个类具体调用的是第二处的重载类,虚拟机调用这个类会解析类引用问题。
第二处
参数的含义:name就是欲加载的类的二进制名字,resolve是否解析类引用
方法含义:加载指定二进制名字的类,该方法的默认加载顺序是
调用findLoadedClass方法检查欲加载的类是否已经加载
调用父加载器的loadClass,如果父加载器为空,那么就会用系统类加载加载
调用findClass去find类
如果使用以上的步骤find到了欲加载的类,并且resolve参数为true,那么该方法就会调用resolveClass方法去解析类。
ClassLoader的子类一般比较倾向于重写findClass方法,而不是此方法。
findClass
//该方法的实现为抛出异常,没有具体实现,因此子类需要实现
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
参数含义:name就是欲加载的类的二进制名字。
方法含义:找到指定名字的class,自定义的类加载器需重写此方法。因为这种方式会符合双亲委托模型,在检查父加载器后,loadingClass方法就会调用此方法。(上述的步骤,因此该方法是虚拟机调用,开发者只需调用loadingClass方法即可)
defineClass
//name:待定义的类的二进制名字
//b: 包含类信息的字节数组
//off:类信息在数组的开始索引
//len: 类信息的长度
Class<?> defineClass(String name, byte[] b, int off, int len) throws ClassFormatError
方法含义:将字节数组转为一个class实例,在一个类被使用之前,class必须被解析。
三 案例使用
使用自定义类加载器去加载指定的类
//欲加载的类
//二进制名字:com.minato.jvm.chapter15.People
public class People {
}
//自定义类加载器需继承自ClassLoader
public class MyTest extends ClassLoader {
private String classLoaderName;
private String fileExtension = ".class";
public MyTest(String classLoaderName) {
this.classLoaderName = classLoaderName;
}
public MyTest(ClassLoader parent, String classLoaderName) {
super(parent);
this.classLoaderName = classLoaderName;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classData = getClassData(name);
return defineClass(null, classData, 0, classData.length);
}
public byte[] getClassData(String className) {
byte[] bytes = null;
InputStream is = null;
ByteArrayOutputStream bos = null;
try {
is = new FileInputStream(new File(className, fileExtension));
bos = new ByteArrayOutputStream();
int ch = 0;
while ((ch = is.read()) != -1) {
bos.write(ch);
}
bytes = bos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
is.close();
bos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return bytes;
}
public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
MyTest myTest=new MyTest("MyLoader");
Class<?> loadClass = myTest.loadClass("com.minato.jvm.chapter15.People");
Object instance = loadClass.newInstance();
System.out.println(instance.getClass().getClassLoader());
//此时打印的类加载器任然是系统类加载器
//原因:系统类加载器加载的是项目中的类,People就是项目中的类,
//按照双亲委托模型:myTest加载类,将任务委托给父加载器(系统类加载器),父加载器发现可以加载,就加载类了,并不需要在给子类去加载。
//故MyTest的findClass方法不会走
//同样的道理,只要加载的类不是项目中的类,findClass方法就会走,因为父加载器都不会加载。实例如下
}
}
加载的类是非项目中的类,只有自定义的类加载器可以加载
//将People移至桌面
public class MyTest extends ClassLoader {
private String classLoaderName;
private String fileExtension = ".class";
private String path;
public MyTest(String classLoaderName) {
super();
this.classLoaderName = classLoaderName;
}
public void setPath(String path) {
this.path = path;
}
public MyTest(ClassLoader parent, String classLoaderName) {
super(parent);
this.classLoaderName = classLoaderName;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
System.out.println("findClass invoke: "+name);
System.out.println("class loader name: "+classLoaderName);
byte[] classData = getClassData(name);
return defineClass(null, classData, 0, classData.length);
}
public byte[] getClassData(String className) {
byte[] bytes = null;
InputStream is = null;
ByteArrayOutputStream bos = null;
className=className.replaceAll("\\.","\\\\");
try {
File file=new File(this.path,className+fileExtension);
is = new FileInputStream(file);
bos = new ByteArrayOutputStream();
int ch = 0;
while ((ch = is.read()) != -1) {
bos.write(ch);
}
bytes = bos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
is.close();
bos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return bytes;
}
public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
MyTest myTest=new MyTest("MyLoader");
myTest.setPath("桌面路径");
Class<?> loadClass = myTest.loadClass("com.minato.jvm.chapter15.People");
Object instance = loadClass.newInstance();
System.out.println(instance.getClass().getClassLoader());
System.out.println(instance);
//打印的类加载器是自定义的MyTest
}
}
网友评论