java之类加载过程讲解
2016-11-06 本文已影响108人
Java面试官
先给源码,再进行分析:
HelloWorld.java
public class HelloWorld{
public static void main(String[] args)
{
System.out.println("https://github.com/wiatingpub/CompanyManager.git");
}
}
CompileClassLoader.java
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
// 用该类来模拟类加载以及类编译的过程
public class CompileClassLoader extends ClassLoader {
//读取指定文件
private byte[] getBytes(String filename) throws IOException {
System.out.println("获取"+filename+".class的字节码。。。");
File file = new File(filename);
long len = file.length();
byte[] raw = new byte[(int) len];
FileInputStream fileInputStream = new FileInputStream(file);
//一次性读取class文件的所有二进制数据
int r = fileInputStream.read(raw);
if (r != len) {
throw new IOException("無法讀取全部文件" + r + " != " + len);
}
return raw;
}
//编译指定的java文件
private boolean compile(String javaFile) throws IOException {
System.out.println(javaFile + "正在编译。。。");
Process process = Runtime.getRuntime().exec("javac " + javaFile);
try {
process.waitFor();
} catch (Exception e) {
// TODO: handle exception
System.out.println(e);
}
int ret = process.exitValue();
return ret == 0;
}
/**
* @Title: findClass
* @Description: 重写父类的findClass方法,进行改写
* @param 同目录下的类名,无后缀,如CompileClassLoader
* @return
* @throws ClassNotFoundException
*/
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class clazz = null;
File javaFile = new File(name + ".java");
File classFile = new File(name + ".class");
// 如果.java文件存在,而.class文件不存在的时候,或者.java文件的最新修改时间
// 比.class的最新修改时间晚的话则进行重新编译
if (javaFile.exists() &&(!classFile.exists() || javaFile.lastModified() > classFile.lastModified())) {
try {
if (!compile(name + ".java") || !classFile.exists()) {
System.out.println("编译失败");
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
//如果.class文件已经存在
if (classFile.exists()) {
try {
//获取.class的所有字节码
byte[] raw = getBytes(name + ".class");
System.out.println("调用ClassLoader的defineClass奖二进制数据转换成class对象。。。");
//调用ClassLoader的defineClass奖二进制数据转换成class对象
clazz = defineClass(name, raw, 0, raw.length);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
if (clazz == null) {
throw new ClassNotFoundException(name);
}
//返回对象
return clazz;
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
if (args.length < 1) {
System.out.println("缺少目标类");
}
String progClass = args[0];
String[] progArgs = new String[args.length - 1];
System.arraycopy(args, 1, progArgs, 0, progArgs.length);
CompileClassLoader compileClassLoader = new CompileClassLoader();
/**
* 当CompileClassLoader调用loadClass的时候程序会执行如下操作:
* 1、使用findLoadedClass来检查是否已经加载类,如果已经加载则直接返回
* 2、在父类加载器上调用loadClass方法,如果父类加载器为null,则使用根类加载器来加载
* 3、调用findClass函数来查找类
*/
Class<?> clazz = compileClassLoader.loadClass(progClass.toString());
//使用反射机制
Method main = clazz.getMethod("main", (new String[0]).getClass());
Object argsArray[] = {progArgs};
main.invoke(null, argsArray);
}
}
如何使用该demo:
将两个类放在同一目录下,打开命令行输入:
javac CompileClassLoader.java (进行编译)
再输入:
java CompileClassLoader HelloWorld (进行运行并传递参数HelloWorld)
分析:
从类加载说起:
1、ClassLoader类加载器调用loadClass加载HelloWorld这个类的时候会执行如下操作:
- 使用findLoadedClass来检查是否已经加载类,如果已经加载则直接返回;
- 在父类加载器上调用loadClass方法,如果父类加载器为null,则使用根类加载器来加载 ;
- 调用findClass函数来查找类;
2、由于CompileClassLoader继承了ClassLoader并且重写了findClass函数,所以当ClassLoader调用了findClass的时候实现的是重写的函数。
3、findClass该函数的作用就是返回一个类,那么如何返回一个类呢?其实这就是一个类编译的过程:
- 需要将一个指定的.java文件编译为.class文件;
- 将.class文件的字节码读取出来
- 调用ClassLoader的defineClass奖二进制数据转换成class对象。
末尾的代码使用到java的反射机制,下一篇文章再提。