算上大学,尽管接触Java已经有4年时间并对基本的API算得上熟练应用,但是依旧觉得自己对于Java的特性依然是一知半解。要成为优秀的Java开发人员,需要深入了解Java平台的工作方式,其中类加载机制和JVM字节码这样的核心特性。今天我将记录一下我在新的学习路程中对Java类加载机制的理解。

1.类加载机制

类加载是一个将类合并到正在运行着的JVM进程中的过程。首先要加载一个类,我们必须先得将类文件加载进来并连接,并且要加上大量的验证,随后会生成一个代表着这个类的class对象,然后就可以通过它去创建新的实例了。这就是我所理解的Java的类加载机制。

经过加载和连接后出来的class对象,说明这个类已经被加载到了JVM中,此后就不会再加载了。

2.类加载器和双亲委派模型

Java平台里有几个经典的类加载器,它们分别在平台的启动和常规操作中承担不同的任务:

根类加载器(Bootstrap ClassLoader)——通常在虚拟机启动不久后实例化,我们可以将其视作JVM的一部分,他的作用通常是负责加载系统的基础jar包(主要是rt.jar),并且它不做验证工作。我们开发者无法直接使用该加载器
    扩展类加载器(Extension ClassLoader)——用来加载安装时自带的标准扩展。一般包括安全性扩展。我们开发者可以直接使用。
    应用(或系统)类加载器(System ClassLoader)——这是应用最广泛的类加载器。它负责加载应用类。
    定制类加载器(Custom ClassLoader)——在更复杂的环境中,我们开发者可以通过继承java.lang.ClassLoader类的方式实现自己的类加载器以满足一些特殊的需求。比如说Spring框架里的封装了自己的类加载器(我会在随后的spring源码学习中遇到的吧)
    线程上下文类加载器(Context ClassLoader)——默认为系统类加载器,可通过Thread.currentThread().setContextClassLoader(ClassLoader)来设置,每个线程都可以将线程上下文类加载器预先设置为父线程的类加载器。这个主要用于打破双亲委派模型,容许父类加载器通过子类加载器加载所需要的类库。

说到双亲委派模型,我们可以通过这个图可知:


如果说一个类加载器收到类加载请求,它并不会马上去找,它会先把这个请求委托给他的父类加载器去完成,只有它的父类加载器反馈说找不到了,它才会自己去找(注:父类加载器它们的默认的目录路径都是不一样的,一个类在虚拟机里面是用它的全限定类名+它的类加载器来确立它的唯一性),采用双亲委派的好处可以保证系统中的类不混乱,如你自己写了一个java.lang.object类,并且路径也放在lib下面,此时编译后会有两个object类了,采用双亲委派就只会加载rt.jar里面的object而不加载你自己写的。

3.加载类

手动加载类有两种方式,Class.forName()和ClassLoader.loadClass()两种:

我们从源码来看看他们的区别

Class.forName()

它有两个重载方法

<pre name="code" class="java">    public static Class<?> forName(String className)
                    throws ClassNotFoundException {
            return forName0(className, true, ClassLoader.getCallerClassLoader());
        }

public static Class<?> forName(String name, boolean initialize,
                                   ClassLoader loader)
        throws ClassNotFoundException
    {
        if (loader == null) {
            SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                ClassLoader ccl = ClassLoader.getCallerClassLoader();
                if (ccl != null) {
                    sm.checkPermission(
                        SecurityConstants.GET_CLASSLOADER_PERMISSION);
                }
            }
        }
        return forName0(name, initialize, loader);
    }

/** Called after security checks have been made. */
        private static native Class<?> forName0(String name, boolean initialize,
                                                ClassLoader 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

Java类加载机制的理解的更多相关文章

  1. 两道面试题,带你解析Java类加载机制

    文章首发于[博客园-陈树义],点击跳转到原文<两道面试题,带你解析Java类加载机制> 在许多Java面试中,我们经常会看到关于Java类加载机制的考察,例如下面这道题: class Gr ...

  2. 【转】两道面试题,带你解析Java类加载机制(类初始化方法 和 对象初始化方法)

    本文转自 https://www.cnblogs.com/chanshuyi/p/the_java_class_load_mechamism.html 关键语句 我们只知道有一个构造方法,但实际上Ja ...

  3. 带你解析Java类加载机制

      目录 Java类加载机制的七个阶段 加载 验证 准备(重点) 解析 初始化(重点) 使用 卸载 实战分析 方法论 树义有话说 在许多Java面试中,我们经常会看到关于Java类加载机制的考察,例如 ...

  4. 你所不知道的库存超限做法 服务器一般达到多少qps比较好[转] JAVA格物致知基础篇:你所不知道的返回码 深入了解EntityFramework Core 2.1延迟加载(Lazy Loading) EntityFramework 6.x和EntityFramework Core关系映射中导航属性必须是public? 藏在正则表达式里的陷阱 两道面试题,带你解析Java类加载机制

    你所不知道的库存超限做法 在互联网企业中,限购的做法,多种多样,有的别出心裁,有的因循守旧,但是种种做法皆想达到的目的,无外乎几种,商品卖的完,系统抗的住,库存不超限.虽然短短数语,却有着说不完,道不 ...

  5. Java类加载机制总结

    关于Java类加载机制的几个基本概念: JDK提供的基本类加载器:引导类加载器(Bootstrap Class Loader)-用于加载JDK中的核心类.扩展类加载器(Ext Class Loader ...

  6. 理解Java类加载机制(译文)

    理解java类加载机制 你想写类加载器?或者你遇到了ClassCastException异常,或者你遇到了奇怪的LinkageError状态约束异常.应该仔细看看java类的加载处理了. 什么是类加载 ...

  7. 深入理解和探究Java类加载机制

    深入理解和探究Java类加载机制---- 1.java.lang.ClassLoader类介绍 java.lang.ClassLoader类的基本职责就是根据一个指定的类的名称,找到或者生成其对应的字 ...

  8. 深入理解Java类加载机制,再也不用死记硬背了

    谈谈"会"的三个层次 在<说透分布式事务>中,我举例里说明了会与会的差别.对一门语言的学习,这里谈谈我理解的"会"的三个层次: 第一层:了解这门语言 ...

  9. Java类加载机制深度分析

    转自:http://my.oschina.net/xianggao/blog/70826 参考:http://www.ibm.com/developerworks/cn/java/j-lo-class ...

随机推荐

  1. 【AtCoder】ARC090

    C - Candies 前一枚举一个i,求第一行的前i个和第二行从第n个到第i个 代码 #include <bits/stdc++.h> #define fi first #define ...

  2. 【AtCoder】AGC028 (A-E)题解

    A - Two Abbreviations 如果有最小答案的话这个答案一定是N和M的lcm 我们考虑一下什么情况下 \(k \frac{L}{N} = h \frac{L}{M}\)且\(k,g\)互 ...

  3. 桌面图形化安装的CentOS6.7中默认安装的yum不能正常使用

    使用rpm -qa|grep yum,可以发现有好多关于yum的安装插件等东西... 从里面将的一些东西删除掉,只留下下面三个即可,其余的全部删除掉rpm -e yum-plugin-security ...

  4. selenium自动测试

    import requestsimport sysimport iofrom selenium import webdriverfrom selenium.webdriver.common.actio ...

  5. 【Ubuntu】Ubuntu设置和查看环境变量

    [Ubuntu]Ubuntu设置和查看环境变量    转载 https://blog.csdn.net/White_Idiot/article/details/78253004 1. 查看环境变量 查 ...

  6. 002.etcd使用场景

    引用链接: https://blog.csdn.net/linuxheik/article/details/77853119 https://www.cnblogs.com/doscho/p/6221 ...

  7. SpringMVC框架03——数据绑定

    1.绑定基本数据类型 在Controller类中添加业务方法: /** * 绑定基本数据类型 */ @RequestMapping("/baseType") @ResponseBo ...

  8. 使用ajax与jqplot的小体会

    在使用ajax与jqplot时遇到了传值的问题!一开始都不知值是怎么传过去的,只找到了例子是以<div id="data">原始数据</div>这样子来接收 ...

  9. 开发人员如何正确对待BUG?

    ‌1.前端开发与后端开发 出了问题,最重要的是先找到方法迅速解决,而不是去互相指责.前端存在这样的思维模式,后端也存在这样的思维模式,这种思维模式不太好.出了问题,最好先检查一下自己,反省是不是自己这 ...

  10. Django-ContentType-signals 实现牛逼玩法

    一.ContentType 在django中,有一个记录了项目中所有model元数据的表,就是ContentType,表中一条记录对应着一个存在的model,所以可以通过一个ContentType表的 ...