算上大学,尽管接触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. Angular 5项目

    Angular 5项目 如果您正在使用angular, 但是没有好好利用angular cli的话, 那么可以看看本文. Angular CLI 官网: https://github.com/angu ...

  2. 【AtCoder】AGC029(A-E)

    A - Irreversible operation 题解 把每个B后面的W个数累加起来即可 代码 #include <bits/stdc++.h> #define fi first #d ...

  3. MapReduce实战1

    MapReduce编程规范: (1)用户编写的程序分成三个部分:Mapper,Reducer,Driver(提交运行mr程序的客户端) (2)Mapper的输入数据是KV对的形式(KV的类型可自定义) ...

  4. P1417 烹调方案 背包DP

    题目背景 由于你的帮助,火星只遭受了最小的损失.但gw懒得重建家园了,就造了一艘飞船飞向遥远的earth星.不过飞船飞到一半,gw发现了一个很严重的问题:肚子饿了~ gw还是会做饭的,于是拿出了储藏的 ...

  5. php返回上一页

    echo "<script>alert('没有获取到订单信息');history.go(-1);</script>";

  6. 关于伪类“:pseudo-class”和伪元素“::pseudo-element”的常见应用

    伪类用于指定要选择的元素的特殊状态,向其添加特殊的效果,比如: input { width: 515px; height: 50px; padding: 10px 20px; border: 1px ...

  7. 用mac的chrome浏览器调试Android手机的网页

    一.参考链接 read chrome remote debugging documentation 调出开发者选项 二.设置android 在安卓4.2及更新的版本中,默认情况下,[开发者选项]是隐藏 ...

  8. python3.5 自带的虚拟环境使用

    首先我们要选择一个目录作为虚拟环境的目录, 这里选择c:\myenv cd myenv python -m venv . #在当前目录下创建虚拟环境 创建完成之后,myenv下会多出一些文件 进入sc ...

  9. HDU.3311.Dig The Wells(DP 斯坦纳树)

    题目链接 \(Description\) 有n座庙.一共n+m个点,可以在任意一些点修建水井,不同位置花费不同:也可以某些点之间连无向边共享水.求使n座庙都有水的最小花费. \(Solution\) ...

  10. Codeforces Round #397 by Kaspersky Lab and Barcelona Bootcamp (Div. 1 + Div. 2 combined) C. Table Tennis Game 2 水题

    C. Table Tennis Game 2 题目连接: http://codeforces.com/contest/765/problem/C Description Misha and Vanya ...