标签:
类加载器负责将.class文件加载到内存中,并为类生成一个java.lang.Class实例。
一旦一个类被加载入JVM中,同一个类就不会被再次加入了。在JVM中用来判断类的唯一性标识是:类名、类所在的包名和类加载器。
当JVM启动时,会形成由三个类加载器组成的初始类加载器层次结构:
根类加载器,负责加载java的核心类(即JAVA_HOME/jre/lib下的jar包)。当使用-Xbootclasspath选项或使用-D选项指定sun.boot.class.path系统属性也可以指定加载附加的类。根类加载器比较特殊,他并不是java.lang.ClassLoader的子类,而是由JVM自身实现的。
扩展类加载器,负责加载JRE的扩展目录(即JAVA_HOME/jre/lib/ext目录)下的jar包。
系统类加载器,负责在JVM启动时,加载来自命令java中的-classpath选项或java.class.path系统属性,或CLASSPATH环境变量所指定的jar包和类路径,或者是当前类路径。程序可以通过ClassLoader的静态方法getSystemClassLoader()方法获取系统类加载器。
除了Java提供的这三种类加载器,开发者也可以实现自己的类加载器,自定义的类加载器通过继承ClassLoader来实现。
JVM中这四种类加载器的层次结构如图:
如图所示:根类加载器是扩展类加载器的父类加载器;扩展类加载器是系统类加载器的父类加载器;如果没有特别指定,用户自定义的类加载器都以系统类加载器作为父加载器。
需要提下,类加载器之间的父子关系并不是类继承上的父子关系,而是类加载器实例之间的关系。
JVM的类加载机制主要有如下三种机制:
类加载器加载类大致要经过8个步骤:
JVM中除了根类加载器之外的类加载器都是ClassLoader的子类的实例。开发者可以通过继承ClassLoader,并重写ClassLoader的方法来实现自定义类加载器。
ClassLoader类有三个关键方法:
要实现自定义的ClassLoader,可以通过重写loadClass或findClass来实现。不过一般推荐重写findClass,而不是loadClass,看一下loadClass的执行步骤:
从上面看来,采用重写findClass的方法可以避免覆盖默认类加载器的父类委托和缓存机制两种策略。
以下是一个自定类加载器的实例:
package com.zhyea.test; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; /** * 自定义类加载器实例 * @author robin * */ public class MyClassLoader extends ClassLoader { /** * 读取类文件 * * @param filePath * 类文件路径 * @return * @throws IOException */ private byte[] getBytes(String filePath) throws IOException { InputStream in = null; try { File file = new File(filePath); long length = file.length(); in = new FileInputStream(file); byte[] buffer = new byte[(int) length]; int hasRead = in.read(buffer); if (hasRead != length) { throw new IOException("无法读取全部文件:" + hasRead + "!=" + length); } return buffer; } finally { if (null != in) in.close(); } } /** * 编译Java文件 * * @param javaFile * Java文件 * @return * @throws IOException * @throws InterruptedException */ private boolean compile(String javaFile) throws IOException, InterruptedException { // 调用系统javac命令 Process p = Runtime.getRuntime().exec("javac " + javaFile); // 强制其他线程等待这个线程完成 p.waitFor(); // 获取javac线程的退出值 int rtn = p.exitValue(); // 返回编译是否成功 return rtn == 0; } /** * 重写ClassLoader的findClass类 */ @Override protected Class<?> findClass(String name) throws ClassNotFoundException{ Class<?> clazz = null; String javaPath = name.replace(".", "/"); String javaFileName = javaPath + ".java"; String classFileName = javaPath = ".class"; File javaFile = new File(javaFileName); File classFile = new File(classFileName); if(javaFile.exists() && (!classFile.exists() || javaFile.lastModified()>classFile.lastModified())){ try{ if(!compile(javaFileName) || !classFile.exists()){ throw new ClassNotFoundException("Class not found : " + javaFileName); } }catch(Exception e){ e.printStackTrace(); } } if(classFile.exists()){ try{ byte[] raw = getBytes(classFileName); clazz = defineClass(name, raw, 0, raw.length); }catch(Exception e){ e.printStackTrace(); } } if(null==clazz){ throw new ClassNotFoundException(name); } return clazz; } public static void main(String[] args) throws Exception{ String javaName = "com.zhyea.test.MyTest"; MyClassLoader mcl = new MyClassLoader(); Class<?> clazz = mcl.loadClass(javaName); Method main = clazz.getMethod("main", (new String[0]).getClass()); Object[] arrs = {args}; main.invoke(null, arrs); } }
再附上演示用的测试类,其实很简单了:
package com.zhyea.test; /** * 自定义类加载器演示用的测试类 * @author robin * */ public class MyTest { public static void main(String[] args) { System.out.println("This is a Test!"); } }
使用自定义的类加载器,可以实现如下功能:
URLClassLoader是扩展类加载器类和系统类加载器类的父类。URLClassLoader功能比较强大,可以从本地获取java文件来加载类,也可获取远程文件来加载类。
演示下:
package com.zhyea.test; import java.net.URL; import java.net.URLClassLoader; public class UCLTest { public static void main(String[] args) throws Exception { URL[] urls = {new URL("file:com.zhyea.test.MyTest.java")}; URLClassLoader ucl = new URLClassLoader(urls); MyTest mt = (MyTest) ucl.loadClass("com.zhyea.test.MyTest").newInstance(); mt.test(); ucl.close(); } } package com.zhyea.test; /** * URLClassLoader演示用的测试类 * @author robin * */ public class MyTest { public void test(){ System.out.println("This is a Test 2!"); } }
标签:
原文地址:http://www.cnblogs.com/amunote/p/4176655.html