后台(30)——类加载器ClassLoader

探索Android软键盘的疑难杂症
深入探讨Android异步精髓Handler
详解Android主流框架不可或缺的基石
站在源码的肩膀上全解Scroller工作机制



Android多分辨率适配框架(1)— 核心基础
Android多分辨率适配框架(2)— 原理剖析
Android多分辨率适配框架(3)— 使用指南



自定义View系列教程00–推翻自己和过往,重学自定义View
自定义View系列教程01–常用工具介绍
自定义View系列教程02–onMeasure源码详尽分析
自定义View系列教程03–onLayout源码详尽分析
自定义View系列教程04–Draw源码分析及其实践
自定义View系列教程05–示例分析
自定义View系列教程06–详解View的Touch事件处理
自定义View系列教程07–详解ViewGroup分发Touch事件
自定义View系列教程08–滑动冲突的产生及其处理


版权声明


类加载器简介

在Java中类加载器ClassLoader负责加载所有的类,该类被加载至内存后会生成一个与之对应的java.lang.Class实例。当类被装载进JVM以后,当需要再次使用同一个类时就不用再次从内存外读取,而是直接使用JVM中的该类。嗯哼,那么怎么样才算是”同一个”类呢?或者说检验是否是”同一个”类的标准是什么呢?在Java中用类的全限定类名(包名+类名)唯一标识一个类;在JVM中用类的全限定类名(包名+类名)并结合加载它的类加载器共同唯一标识一个类。比如,tokyo.hot包下有个AVer类,该类被ClassLoader1加载进JVM;与此同时该类又被ClassLoader2加载进JVM;那么它们两者在JVM中对应的Class的唯一标识分别为(AVer,tokyo.hot,ClassLoader1)和(AVer,tokyo.hot,ClassLoader2)。这意味着同一个类被两个不同的类加载器加载至JVM时,这两个类彼此独立,互不影响是两个完全不同的Class

嗯哼,那么Java中是不是有个万能的类加载器ClassLoader可以将任何类加载进JVM呢?非也!!!JVM启动时会生成三个类加载器:

  • 根类加载器:Bootstrap ClassLoader
  • 扩展类加载器:Extension ClassLoader
  • 系统类加载器:System ClassLoader

现在,我们来依次学习这三个类加载器


Bootstrap ClassLoader

Bootstrap ClassLoader常被称为根(原始、引导)类加载器,负责加载Java的核心类。至于它具体加载了哪些Java核心类呢?我们来通过一个例子看看

/**
 * 本文作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
package cn.com;
import java.net.URL;
import sun.misc.URLClassPath;

public class TestBootstrap {
    public static void main(String[] args) {
        URLClassPath bootstrapClassPath = sun.misc.Launcher.getBootstrapClassPath();
        URL[] urls = bootstrapClassPath.getURLs();
        for (int i = 0; i < urls.length; i++) {
            System.out.println(urls[i].toExternalForm());
        }
    }
}

在编写这段代码时Eclipse可能会报错:Access restriction: The type ‘Launcher’ is not API。该问题可以这么解决:Project -> Properties -> libraries然后remove原来的JRE System Library,然后再Add Library重新添加一次即可。

打印结果如下:

file:/C:/Program Files/Java/jre1.8.0_60/lib/resources.jar
file:/C:/Program Files/Java/jre1.8.0_60/lib/rt.jar
file:/C:/Program Files/Java/jre1.8.0_60/lib/sunrsasign.jar
file:/C:/Program Files/Java/jre1.8.0_60/lib/jsse.jar
file:/C:/Program Files/Java/jre1.8.0_60/lib/jce.jar
file:/C:/Program Files/Java/jre1.8.0_60/lib/charsets.jar
file:/C:/Program Files/Java/jre1.8.0_60/lib/jfr.jar
file:/C:/Program Files/Java/jre1.8.0_60/classes

咦,看到这些,我们还是觉得很陌生。是么?来,我们一起来看看熟悉的

请打开rt.jar后再打开java.lang,嗯哼,是不是看到我们熟悉的东西了String.class、Thread.class、Integer.class、Object等等。这些Java核心类正是由Bootstrap ClassLoader加载的。


Extension ClassLoader

Extention ClassLoader被称为扩展类加载器,它负责加载JRE的扩展目录(%JAVA_HOME%/jre/lib/ext)中JAR包的类。所以,可将我们自己开发好的JAR包放入该路径下从而为java扩展核心类以外的功能。


System ClassLoader

System ClassLoader被称为系统(应用)类加载器,它负责在JVM启动时加载CLASSPATH所指定的JAR包和类路径。我们可通过ClassLoader的静态方法getSystemClassLoader()获取系统类加载器。一般情况下,我们编写的java类都是由这个类加载器加载。


类加载器的工作机制

刚才我们已经介绍了这三种类加载器,我们现在通过代码的形式来再次认识它们:

/**
 * 本文作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
package cn.com;

import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;

public class TestClassLoader {
    public static void main(String[] args) throws IOException {
        // 获取系统类加载器
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
        System.out.println("系统类加载器SystemClassLoader="+classLoader);
        Enumeration<URL> urlEnumeration = classLoader.getResources("");
        while (urlEnumeration.hasMoreElements()) {
            URL nextElement = urlEnumeration.nextElement();
            System.out.println("系统类加载器SystemClassLoader的加载路径="+nextElement);
        }
        //获取扩展类加载器
        ClassLoader extLoader = classLoader.getParent();
        System.out.println("扩展类加载器ExtClassLoader=" + extLoader);
        String property = System.getProperty("java.ext.dirs");
        System.out.println("扩展类加载器ExtClassLoader的加载路径=" + property);
        //获取扩展类加载器的父类
        ClassLoader bootstrapClassLoader=extLoader.getParent();
        if(bootstrapClassLoader==null){
            System.out.println("扩展类加载器的父类=null");
        }
    }
}

输出如下:

系统类加载器SystemClassLoader=sun.misc.Launcher$AppClassLoader@4e0e2f2a
系统类加载器SystemClassLoader的加载路径=file:/D:/Workspace/TestClassLoader01/bin/
扩展类加载器ExtClassLoader=sun.misc.Launcher$ExtClassLoader@5c647e05
扩展类加载器ExtClassLoader的加载路径=C:\Program Files\Java\jre1.8.0_60\lib\ext;C:\WINDOWS\Sun\Java\lib\ext
扩展类加载器的父类=null

在这我们可以看出来:

  • 系统类加载器的加载路径是当前程序运行的路径
  • 扩展类加载器的加载路径是C:\Program Files\Java\jre1.8.0_60\lib\ext
  • 系统类加载器的父类是扩展类加载器
  • 扩展类加载器的getParent()返回值为null,并不是我们预计的根类加载器。这是因为根类加载器Bootstrap ClassLoader没有继承java.lang.ClassLoader抽象类。当然,实际上扩展类加载器的父类加载器就是根加载器,只不过根加载器不是Java实现的而是由JVM自身实现的。

在明白了这些之后,我们再来看类加载器中的父类委托机制。

当一个类加载器收到了类加载的请求时它不会自己去尝试加载这个类,而是把该请求委派给父加载器去完成。这样一层一层地往上传递,最终所有的加载请求都被传到最顶层的根类加载器中。只有当父加载器无法完成这个加载请求时,父加载器再将该请求交给子加载器去尝试加载。

这么做有什么好处呢?
当一个类加载器负责加载某个Class时,该Class所依赖和应该的其它Class也将由该类加载器载入。我们知道java.lang.Object是所有类的父类,因此无论哪个类加载都要加载这个类,于是关于加载Object的所有的请求都汇聚到了顶层的根类加载器中,因此Object类会由根类加载器来加载。所以在一次Object类后,就不用再次加载该类了。如果不采用该父类委托模式而由各个类加载器自行加载的话,系统中就会出现多个Object类,不仅效率低下而且混乱不堪也不能保证加载的对象是同一个。

时间: 2024-09-20 00:02:40

后台(30)——类加载器ClassLoader的相关文章

一篇文章读懂Java类加载器

Java类加载器算是一个老生常谈的问题,大多Java工程师也都对其中的知识点倒背如流,最近在看源码的时候发现有一些细节的地方理解还是比较模糊,正好写一篇文章梳理一下. 关于Java类加载器的知识,网上一搜一大片,我自己也看过很多文档,博客.资料虽然很多,但还是希望通过本文尽量写出一些自己的理解,自己的东西.如果只是重复别人写的内容那就失去写作的意义了. 类加载器结构 类加载器结构 名称解释: 根类加载器,也叫引导类加载器.启动类加载器.由于它不属于Java类库,这里就不说它对应的类名了,很多人喜

Java类加载器以及类加载器的委托模型

我们知道,我们在Java中用到的所有的类都是通过类加载器ClassLoader加载到JVM中的,我们还知道类加载器也对应着一个类,既然这样那么我们会想那么ClassLoader类是由谁加载的呢? 其实在Java中有许许多多的类加载器,我们甚至可以写自己的类加载器. 其中主要三个类加载器(他们是树形关系)是: BootStrap:在java虚拟机启动的时候会利用这个类加载器来加载 JDK安装目录下的 /JRE/LIB/rt.jar 也就是系统默认导入的一些类例如System类,这个类加载器不是类

热点推荐:Java类加载器深入理解

首先回顾一下,java虚拟机载入java类的步骤:java文件经过编译器编译后变成字节码文件(.class文件),类加载器 (ClassLoader)读取.class文件,并且转换成java.lang.Class的一个实例,最后通过newInstance方法创建该类的 一个对象.ClassLoader的作用就是根据一个类名,找到对应的字节码,根据这些字节码定义出对应的类,该类就是java.lang.Class的 一个实例. 类加载器的组织结构 java有三个初始类加载器,当java虚拟机启动时,

分析Java的类加载器与ClassLoader(二):classpath与查找类字节码的顺序,分析ExtClassLoader与AppClassLoader的源码

先回顾一下classpath classpath的作用:         classpath的作用是指定查找类的路径:当使用java命令执行一个类(类中的main方法)时,会从classpath中进行查找这个类.  指定classpath的方式一:         设置环境变量CLASSPATH,多个路径之间使用英文的分号隔开,也可以指定为jar包路径.          示例:CLASSPATH=c:/myclasses/;c/mylib/aa.jar;c:/mylib/bb.jar;.  

类加载器与单例

当使用不同的类加载器时,也会使单例失效,如下:  单例为:  ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public final class Singleton{           private static final Singleton instance=new Singleton();           private Singleton(){                 System.out.println("执行构造函数");      

JAVA加密解密:自定义类加载器应用

最近在研究JAVA CLASS LOADING技术,已实现了一个自定义的加载器.对目前自定义加载器的应用,还在探讨中.下面是自定义的CLASSLOADER在JAVA加密解密方面的一些研究. JAVA安全 JAVA是解释执行的语言,对于不同的操作平台都有相应的JVM对字节码文件进行解释执行.而这个字节码文件,也就是我们平时所看到的每一个.class文件. 这是我们大家都知道的常识,也就是由.java文件,经过编译器编译,变成JVM所能解释的.class文件. 而这个过程,在现在公开的网络技术中,利

深入探讨Java类加载器

类加载器是 Java 语言的一个创新,也是 Java 语言流行的重要原因之一. 它使得 Java 类可以被动态加载到 Java 虚拟机中并执行.类加载器从 JDK 1.0 就出现了,最初是为了满足 Java Applet 的需要而开发出来的.Java Applet 需要从远程下载 Java 类文件到浏览器中并执行.现在类加载器在 Web 容器和 OSGi 中得到了广泛的使用.一般来说,Java 应用的开发人员不需要直接同类加 载器进行交互.Java 虚拟机默认的行为就已经足够满足大多数情况的需求

类加载器特技:OSGi代码生成

我们将按照复杂性增加的顺序考察一些类加载的典型问题,开发一小段代码 来解决这些问题中最有趣的一个.即使你不打算马上写一个代码生成框架,这篇 文章也会让你对静态定义依赖的模块运行时(如OSGi系统)的低级操作有比较深 入的了解. 这篇文章还包括一个可以工作的演示项目,该项目不仅包含这里演示的代码 ,还有两个基于ASM的代码生成器可供实践. 类加载地点转换 把一个框架移植到OSGi系统通常需要把框架按照extender模式重构.这个模 式允许框架把所有的类加载工作委托给OSGi框架,与此同时保留对应

java自定义类加载器

v前言 java反射,最常用的Class.forName()方法.做毕设的时候,接收到代码字符串,通过 JavaCompiler将代码字符串生成A.class文件(存放在classpath下,也就是eclipse项目中的bin目录里),然后通过java反射机制,获取main方法并执行..class文件名称固定.当 A.class文件更新的时候,问题出现了,main方法的执行结果总和第一次的执行结果相同. v程序流程 代码提交->接收代码->编译成A.class文件->java反射->