本博文主要讲诉java中类的加载器的使用
类在jvm中的形式:
在JAVA的运行时环境中(Java
java.lang.Class
一旦一个类被载入JVM中,同一个类就不会再次载入了。在java中同一类用其完全匹配类名作为标志,即包名+类名;而在JVM中一个类用其全名和一个加载类ClassLoader的实例作为唯一标识。因此如果一个为P的保重,有一个名为C的类,被加载器ClassLoader的一个实例CL加载,于是C.class在JVM中的表示为(C,P,CL)。
类加载器:
Java中默认有三种类加载器:引导类加载器,扩展类加载器,系统类加载器(也叫应用类加载器)
引导类加载器负责加载jdk中的系统类,如java.lang.Object。这种类加载器都是用c语言实现的,属于系统底层执行动作,在java程序中没有办法获得这个类加载器。如获取java.lang.String.class.getClassLoader()得到为null。
扩展类加载器负责加载标准扩展类,一般使用java实现,这是一个真正的java类加载器,负责加载java.ext.dirs属性下的类,一般为jre/lib/ext中的类。
系统类加载器,加载第一个应用类的加载器,也就是执行java
类加载器之间有一定的关系(父子关系),我们可以认为扩展类加载器的父加载器是引导类加载器(当然不这样认为也是可以的,因为引导类加载器表现在java中就是一个null),不过系统类加载器的父加载器一定是扩展类加载器,类加载器在加载类的时候会先给父加载器一个机会,只有父加载器无法加载时才会自己去加载。
类加载器的方式:
用户定义的类一般都是系统类加载器加载的,我们很少直接使用类加载器加载类,我们甚至很少自己加载类。因为类在使用时会被自动加载,我们用到某个类时该类会被自动加载,比如new
我们也可以使用系统类加载器手动加载类,ClassLoader提供了这个接口ClassLoader.getSystemClassLoader().loadClass("classFullName");这就很明确的指定了使用系统类加载器加载指定的类,但是如果该类能够被扩展类加载器加载,系统类加载器还是不会有机会的。
常用的方式还有使用Class.forName加载使用的类,这种方式没有指定某个特定的ClassLoader,会使用调用类的ClassLoader。也就是说调用这个方法的类的类加载器将会用于加载这个类。比如在类A中使用Class.forName加载类B,那么加载类A的类加载器将会用于加载类B,这样两个类的类加载器是同一个。
类加载的工作流程:
除了引导类加载器,所有的类加载器都有一个父类加载器,不仅如此,所有的类加载器也都是java.lang.ClassLoader类型。使用loadClass()方法可以从类加载器获取该类,可以通过java.lang.ClassLoader的源代码来了解该方法工作的细节,如下:
protected
throws
//
Class
if
try
if
c
}
c
}
}
//
//
c
}
}
if
resolveClass(c);
}
return
}
类加载的Main实例:(转)
上图显示了一个主类称为MyMainClass的应用程序。其中MyMainClass.class会被AppClassLoader加载。MyMainClass创建了两个类加载器的实例:CustomClassLoader1
Target
以上代码会抛出一个ClassCastException。这是因为JVM把他们视为分别不同的类,因为他们被不同的类加载器所定义。这种情况当我们不是使用两个不同的类加载器CustomClassLoader1
创建自己的类加载器
在
defineClass():这个方法用来完成从Java字节代码的字节数组到java.lang.Class的转换。这个方法是不能被覆写的,一般是用原生代码来实现的。
findLoadedClass():这个方法用来根据名称查找已经加载过的Java类。一个类加载器不会重复加载同一名称的类。
findClass():这个方法用来根据名称查找并加载Java类。
loadClass():这个方法用来根据名称加载Java类。
resolveClass:这个方法用来链接一个Java类。
这里比较
前面提到,类加载器的代理模式默认使用的是父类优先的策略。这个策略的实现是封装在loadClass()方法中的。如果希望修改此策略,就需要覆写loadClass()方法。
下面的代码给出了自定义的类加载的常见实现模式:
public
}
由于博主知识有限,如有误,请指正点评,欢迎交流