转自:
Java程序在运行的时候,JVM通过类加载机制(ClassLoader)把class文件加载到内存中,只有class文件被载入内存,才能被其他class引用,使程序正确运行起来.
Java中的ClassLoader有三种:Bootstrap ClassLoader 、Extension ClassLoader、App ClassLoader。
由C++写的,由JVM启动.
启动类加载器,负责加载java基础类,对应的文件是%JRE_HOME/lib/ 目录下的rt.jar、resources.jar、charsets.jar和class等
Java类,继承自URLClassLoader 扩展类加载器,
对应的文件是 %JRE_HOME/lib/ext 目录下的jar和class等
Java类,继承自URLClassLoader 系统类加载器,
对应的文件是应用程序classpath目录下的所有jar和class等
Java的加载机制是双亲委派机制来加载类,
为什么要使用这种方式?这个是为了保证 如果加载的类是一个系统类,那么会优先由Bootstrap ClassLoader 、Extension ClassLoader先去加载,而不是使用我们自定义的ClassLoader去加载,保证系统的安全!
这三种类加载器存在父子关系,App ClassLoader的父类加载器是Extension ClassLoader,Extension ClassLoader的父类加载器是Bootstrap ClassLoader,要注意的一点是,这里的父子并不是继承关系.
public class ClassLoaderParentMain {
public static void main(String[] args){
ClassLoader ClassLoader1 = ClassLoaderParentMain.class.getClassLoader();
ClassLoader ClassLoader2 = ClassLoader1.getParent();
ClassLoader ClassLoader3 = ClassLoader2.getParent();
System.out.println(ClassLoader1);
System.out.println(ClassLoader2);
System.out.println(ClassLoader3);
}
}
输出的结果为:
当这三者中的某个ClassLoader要加载一个类时,会先委托它的父类加载器尝试加载,一直往上,如果最顶级的父类加载器没有找到该类,那么委托者则亲自到特定的地方加载
protected Class> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) { // 首先,检测是否已经加载
Class> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) { //父加载器不为空则调用父加载器的loadClass
c = parent.loadClass(name, false);
} else { //父加载器为空则调用Bootstrap Classloader
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader
}
if (c == null) { // If still not found, then invoke findClass in order // to find the class.
long t1 = System.nanoTime(); //父加载器没有找到,则调用findclass
c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) { //调用resolveClass()
resolveClass(c);
}
return c;
}
}
先加载Extension ClassLoader ,如果没有加载到,那么使用Bootstrap ClassLoader去加载,如果都没有,那么使用App ClassLoader去加载。如果没找到,那么就抛出异常ClassNotFoundException.
这里画张图来表示下
只有被同一个类加载器实例加载并且文件名相同的class文件才被认为是同一个class.
1、为什么要自定义ClassLoader
因为系统的ClassLoader只会加载指定目录下的class文件,如果你想加载自己的class文件,那么就可以自定义一个ClassLoader.
而且我们可以根据自己的需求,对class文件进行加密和解密。
2.如何自定义ClassLoader
2.1
新建一个类继承自java.lang.ClassLoader,重写它的findClass方法。
2.2
将class字节码数组转换为Class类的实例
2.3
调用loadClass方法即可
下面的代码就是自定义Class的例子
public class MyClassLoader extends ClassLoader {
//指定路径
private String path ;
public MyClassLoader(String classPath){
path=classPath;
}
/**
* 重写findClass方法
* @param name 是我们这个类的全路径
* @return
* @throws ClassNotFoundException
*/
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
Class log = null;
// 获取该class文件字节码数组
byte[] classData = getData();
if (classData != null) {
// 将class的字节码数组转换成Class类的实例
log = defineClass(name, classData, 0, classData.length);
}
return log;
}
/**
* 将class文件转化为字节码数组
* @return
*/
private byte[] getData() {
File file = new File(path);
if (file.exists()){
FileInputStream in = null;
ByteArrayOutputStream out = null;
try {
in = new FileInputStream(file);
out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int size = 0;
while ((size = in.read(buffer)) != -1) {
out.write(buffer, 0, size);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return out.toByteArray();
}else{
return null;
}
}
}
可以再getData里面做很多事情 ,比如加密解密之类的 都是可以的。
1、我们生成一个.class文件的测试样品
public class Log {
public static void main(String[] args) {
System.out.println("load Log class successfully");
}
}
2、我们执行命令行 javac Log.java 生成我们的Log.class文件
3、再main函数入口 写配置参数
public class ClassLoaderMain {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
//这个类class的路径
String classPath = "/Users/mac/CommProjects/ClassLoaderDemo/src/com/xuhuawei/classloaderdemo/Log.class";
MyClassLoader myClassLoader = new MyClassLoader(classPath);
//类的全称
String packageNamePath = "com.xuhuawei.classloaderdemo.Log";
//加载Log这个class文件
Class> Log = myClassLoader.loadClass(packageNamePath);
System.out.println("类加载器是:" + Log.getClassLoader());
//利用反射获取main方法
Method method = Log.getDeclaredMethod("main", String[].class);
Object object = Log.newInstance();
String[] arg = {"ad"};
method.invoke(object, (Object) arg);
}
}
输出的结果为: