Java基础Spring BootJava学习笔记

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这个类的时候会执行如下操作:

2、由于CompileClassLoader继承了ClassLoader并且重写了findClass函数,所以当ClassLoader调用了findClass的时候实现的是重写的函数。

3、findClass该函数的作用就是返回一个类,那么如何返回一个类呢?其实这就是一个类编译的过程:

末尾的代码使用到java的反射机制,下一篇文章再提。

上一篇下一篇

猜你喜欢

热点阅读