进大学的话,接触Java已经4年了,虽然已经习惯了基本的API,但是我觉得对Java的特性还是了解了一半。 要成为优秀的Java开发人员,必须深刻理解类加载机制和具有JVM字节码等核心特性的Java平台的工作原理。 今天我将记录我在新的学习过程中对Java类加载机制的了解。
1 .类加载机制
类加载是将类合并到正在运行的JVM进程中的过程。 要首先加载类,必须先加载类文件并连接,然后进行大量的验证。 然后,将生成一个表示该类的class对象,从中可以创建新的实例。 这就是我理解的Java类加载的结构。
通过加载和连接出现的class对象表示该类已经加载到JVM中,以后将不再加载。
2 .类加载器和父母委托模型
Java平台有几种经典的类加载器,它们在平台启动和正常操作中承担着不同的任务。
根类加载器(Bootstrap ClassLoader ) ——通常在虚拟机启动后立即实例化。 它可以被视为JVM的一部分,通常负责加载系统的基本jar包(主要是rt.jar ),而不进行验证工作。 开发人员不能直接使用此加载程序
扩展类加载器(Extension ClassLoader ) ——用于加载安装过程中附带的标准扩展。 一般包括安全扩展。 我们的开发者可以直接使用。
应用(或系统)类加载器(System ClassLoader ) ——这是最广泛使用的类加载器。 负责加载APP应用程序类。
在比自定义类加载器(Custom ClassLoader ) ——更复杂的环境中,开发人员可以通过继承java.lang.ClassLoader类来实现自己的类加载器以满足特定的需要。 例如,Spring框架封装了自己的类加载器。 在之后的Spring源代码学习中遇到了吧。
线程上下文类加载器(Context ClassLoader ) ——缺省为系统类加载器,可以在Thread.currentThread ).setcontextclassloader中进行设置可以为每个线程加载线程上下文类。这主要用于打破父级委托模型,允许父级类加载器通过子类加载所需的类库。
父母委托模型后,我们从这张图中可以看到以下内容。
如果类加载器收到类加载请求,但他说不会立即去找,那么只有先委托他的父加载器完成该请求,并反馈说找不到其父加载器,他才会自己去找。 (注意:父加载器的默认目录路径不同,一个类在虚拟机中通过完全限定类名的类加载器建立唯一性。 )采用父母委托的好处可以保证系统中的类不会混乱。 例如,如果您自己编写了java.lang.object类,并将路径也放在lib下,则编译后将有两个object类。 如果采用来自父母的委托,则不会加载你自己写的object,而只加载rt.jar中的object。
3 .加载类
有两种方法可以手动加载类: Class.forName (和ClassLoader.loadClass )。
从源代码中看看他们的区别吧
Class.forName (
有两种重载方法
publicstaticclassforname (string class name ) throws ClassNotFoundException {
返回格式0 (class name,true,class loader.getcallerclassloader () );
}
publicstaticclassforname (string name,boolean initialize,
ClassLoader loader )
throws ClassNotFoundException
{
if (loader==空值) {
securitymanagersm=system.getsecuritymanager (;
if(sm!=null ) {
class loader ccl=class loader.getcallerclassloader (;
if(ccl!=null ) {
sm.checkPermission (
security constants.get _ class loader _ permission;
}
}
}
returnforname0(name,initialize,loader );
}
/* * calledaftersecuritycheckshavebeenmade.* /
privatestaticnativeclassforname0(字符串名称,布尔初始化,
类
oader loader)throws ClassNotFoundException;
第一个方法默认初始化类。
第二个方法可以选择是否初始化类和可以选择类加载器。
它们俩最终都是返回forName0,而forName0有native关键字,原生态的方法,是其他语言实现的,我就不深究下去了。
ClassLoader.loadClass()
它同样也有两个重载方法
public Class> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
protected Class> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
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();
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(c);
}
return c;
}
}
第一个方法的具体实现是第二个方法。resolve参数为false。
第二个方法先检查这个类是否被加载过,如果没有被加载过,那就通过双亲委派的模式去找,如果父类加载器不是根类加载器,那就递归调用,如果是,那就试着返回根类加载器所加载的类或者返回null,当虚拟机想要装入的不仅包括指定的类似,resolve参数为true,装入该类英语的所有其他类。这个方法还用了其他的类,它们基本都是用native修饰的,在这也不深究,知道是用来干嘛的就好了。
以上就是我所学习的关于Java类加载器的内容。
https://blog.csdn.net/donggua3694857/article/details/51932630