做个笔记,马克一下。
原理(大概)
类加载器的分类
- 启动类加载器(Bootstrap ClassLoader):
    
- 主要负责加载
<JAVA_HOME>\lib目录,或是-Xbootclasspath参数指定的路径中的类库到虚拟机内存中。 System.getProperty("sun.boot.class.path")
 - 主要负责加载
 - 扩展类加载器(Extension ClassLoader):
    
- 主要负责加载
<JAVA_HOME>\lib\ext目录,或者被java.ext.dirs系统变量所指定的路径中的所有类库。 System.getProperty("java.ext.dirs")
 - 主要负责加载
 - 应用程序类加载器/系统类加载器(Application ClassLoader):
    
- 主要负责加载ClassPath路径上的类库,是默认的类加载器。
 System.getProperty("java.class.path")
 
类的加载
- 类加载器的双亲委派模型
    
- 不多说了,启动类加载器优先级最高,它找到了加载类就是加载的它指定的那个了。扩展类加载器次之,系统类加载器最次。
 
 
关于Xbootclasspath参数
- -Xbootclasspath:
    
- 完全取代基本核心的Java class 搜索路径。要重新写所有Java 核心class
 
 - -Xbootclasspath/a:
    
- 后缀在核心class搜索路径后面
 
 - -Xbootclasspath/p:
    
- 前缀在核心class搜索路径前面
 
 
自定义类加载器
继承自java.lang.ClassLoader类并覆写对应的方法即可。
有趣的用途(大概)
Hook某些java应用
比如,现在我要hook原生的输出方法java.io.PrintStream.println()。
当我调用这个方法的时候,会输出hhhhhhh再加上换行符。
- 代码框架如下:

 - 对
java.io.PrintStream的改动如下,其它原样复制
 - 
    
入口测试类:
package test; public class TestPrint { public static void main(String[] args) { System.out.println("----TestPrint begin----"); System.out.println(); System.out.println("----TestPrint end----"); } } - 接下来不多说,直接放结果
    
- java -jar test.jar

 - java -Xbootclasspath/p:test.jar -jar test.jar

 
 - java -jar test.jar
 
动态编译/加载(刷题实现原理?
怎样做一个像LeetCode那样的Java刷题功能呢?
- 一个简单的方法是:
    
- 将提交的题目编译
.class文件到当前ClassPath路径下 - 系统类加载器直接加载类
 - 通过反射调用约定的方法,得到结果
 - 
        
比较预期和实际结果,返回响应
 - 下面是一个例子:来自java实现动态编译并动态加载
        
public void complierAndRun() { try { System.out.println(System.getProperty("user.dir")); // 动态编译 JavaCompiler javac = ToolProvider.getSystemJavaCompiler(); int status = javac.run(null, null, null, "-d", System.getProperty("user.dir") + "\\target\\classes", "D:/test/AlTest.java"); if (status != 0) { System.out.println("没有编译成功!"); } // 通过系统类加载器直接加载类 Class clz = Class.forName("AlTest"); // 通过反射动态执行 Object o = clz.newInstance(); Method method = clz.getDeclaredMethod("sayHello"); // method.invoke() :静态方法第一个参数可为null,第二个参数为实际传参 String result = (String) method.invoke(o); System.out.println(result); } catch (Exception e) { logger.error("test", e); } } 
 - 将提交的题目编译
 - 当用户/题目较多时,显然需要再变更一下,稍微再复杂一点:
    
- 将提交的题目编译
.class文件到指定路径下 - 自定义ClassLoader加载类
 - 通过反射调用约定的方法,得到结果
 - 
        
比较预期和实际结果,返回响应
 - 下面是大概的要点:
        
public class MyClassLoader extends ClassLoader { protected Class<?> findClass(String name) throws ClassNotFoundException { // 读取name对应.class文件到b byte[] b = null; ... return defineClass(name, b, 0, b.length); } public static void main(String[] args) { ... try { // 重载ClassLoader类 MyClassLoader loader = new MyClassLoader(); Class<?> c = loader.findClass(className);// 类名字 c.getMethod(methodName).invoke(c.newInstance());// 方法名字 } catch (Exception e) { e.printStackTrace(); } } } 
 - 将提交的题目编译