Java 之 类加载器

一、类加载器概述

  在开发中会遇到 java.lang.ClassNotFoundException 和 java.lang.NoClassDefError,想要更好解决这类问题,或者在一些特殊的应用场景,比如需要支持类的动态加载或需要对编译后的字节码文件进行加密解密操作,那么需要你自定义类加载器,因此了解类加载器及其加载机制成为了Java开发必备技能之一。

二、四种类加载器

  1、引导类加载器(Bootstrap Classloader),又称为根类加载器

     它负责加载 Java 的核心库(JAVA_HOME/jre/lib/rt.jar 等或 sun.boot.class.path 路径下的内容),是用原生代码(C/C++)来实现的,并不继承自 java.lang.ClassLoader,所以通过 Java 代码获取引导类加载器对象将会得到 null。(只有核心类库如 String 才使用 引导类加载器)

  2、扩展类加载器(Extension Classloader)

     它由 sun.misc.Launcher$ExtClassLoader 实现,是 java.lang.ClassLoader 的子类,负责加载 Java 的扩展库(JAVA_HOME/jre/ext/*.jar或java.ext.dirs路径下的内容)

  3、应用程序类加载器(Application Classloader)

    它由 sun.misc.Lanuncher$AppClassLoader 实现,是 java.lang.ClassLoader 的子类,负责加载 Java 应用程序类路径(classpath、java.class.path)下的内容。(通俗的讲:项目的路径bin文件夹下的字节码,以及如果你配置了环境变量classpath)

  4、自定义类加载器

    开发人员可以通过继承java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求,例如对字节码进行加密来避免class文件被反编译,或者加载特殊目录下的字节码数据。

  小结:类加载器是用来完成类加载的。

三、经典委托模式

  1、Java 中类加载器的双亲委托模式

    类加载器负责加载所有的类,系统为所有被载入内存中的类生成一个java.lang.Class实例。一旦一个类被载入JVM中,同一个类就不会被再次载入了。

    那么,怎么样算是“同一个类”呢?

    在JVM中,一个类用其全限定类名和其类加载器作为其唯一标识。换句话说,同一个类如果用两个类加载器分别加载,JVM将视为“不同的类”,它们互不兼容。

    那么,我们的类加载器在执行类加载任务的时候,如何确保一个类的全局唯一性呢?

    Java虚拟机的设计者们通过一种称之为“双亲委派模型(Parent Delegation Model)”的委派机制来约定类加载器的加载机制。

    按照双亲委派模型的规则,除了引导类加载器之外,程序中的每一个类加载器都应该拥有一个超类加载器,比如:ExtClassLoader的超类加载器是引导类加载器,而AppClassLoader的超类加载器是ExtClassLoader,而自定义类加载器的超类就是AppClassLoader。

    

      那么当一个类加载器接收到一个类加载任务的时候,它并不会立即展开加载,先检测此类是否加载过,即在方法区寻找该类对应的Class对象是否存在,如果存在就是已经加载过了,直接返回该Class对象,否则会将加载任务委派给它的超类加载器去执行,每一层的类加载器都采用相同的方式,直至委派给最顶层的启动类加载器为止,如果超类加载器无法加载委派给它的类时,便会将类的加载任务退回给它的下一级类加载器去执行加载,如果所有的类加载器都加载失败,就会报java.lang.ClassNotFoundException或java.lang.NoClassDefFoundError。

    

    在此大家需要注意,由于Java虚拟机规范并没有要求类加载器的加载机制一定要使用双亲委托模式,只是建议采用这种方式而已。比如在Tomcat中,类加载器所采用的加载机制就和传统的双亲委派模型有一定区别,当缺省的类加载器就接收到一个类的加载任务时,首先会由它自行加载,当它加载失败时,才会将类的加载任务委派给它的超类加载器去执行,这同时也是Servlet规范推荐的一种做法。

说明:

数组类型本身并不是由类加载器负责创建,而是由JVM在运行时根据需要而直接创建的,但数组的元素类型仍然需要依靠类加载器去创建。因此,JVM会把数组元素类型的类加载器记录为数组类型的类加载器。    

  2、双亲委托模式目的是什么?

    目的:为了安全,而且各司其职,保证核心类库的安全性

     当我们自己声明一个 java.lang.String 类,类加载器会为我们加载自定义的String类还是系统的String类呢?

当应用程序类加载器接到加载某个类的任务时,例如:java.lang.String。
(1)会现在内存中,搜索这个类是否加载过了,如果是,就返回这个类的Class对象,不去加载。
(2)如果没有找到,即没有加载过。会把这个任务先提交给“父加载器”
 
 当扩展类加载器接到加载某个类的任务时,例如:java.lang.String。
(1)会现在内存中,搜索这个类是否加载过了,如果是,就返回这个类的Class对象,不去加载。
(2)如果没有找到,即没有加载过。会把这个任务先提交给“父加载器”
 
 当引导类加载器接到加载某个类的任务时,例如:java.lang.String。
(1)会现在内存中,搜索这个类是否加载过了,如果是,就返回这个类的Class对象,不去加载。
(2)如果没有找到,即没有加载过。会在它的负责的范围内尝试加载。
 如果可以找到,那么就返回这个类的Class对象。就结束了。
 如果没有找到,那么会把这个任务往回传,让“子加载器”扩展类加载器去加载。
 
 “子加载器”扩展类加载器接到“父加载器”返回的任务后,去它负责的范围内加载。
 如果可以找到,那么就返回这个类的Class对象。就结束了。
 如果没有找到,那么会把这个任务往回传,让“子加载器”应用程序类加载器去加载。
 
 “子加载器”应用程序类加载器接到“父加载器”返回的任务后,去它负责的范围内加载。
 如果可以找到,那么就返回这个类的Class对象。就结束了。
 如果没有找到,那么就报错ClassNotFoundException或java.lang.NoClassDefError

四、类加载器的作用

  1、本质工作

     类加载器的本质工作就是用于加载类

  2、加载文件

    类加载器还可以用来加载“类路径下”的资源文件。

原文地址:https://www.cnblogs.com/niujifei/p/12310932.html

时间: 2024-11-02 12:57:44

Java 之 类加载器的相关文章

java自定义类加载器

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

分析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;.          注意

java 用类加载器的方式管理资源和配置文件

public class ReflectTest {public static void main(String[] args) throws Exception {    //config.properties 与当前类在不同包下:    InputStream is=ReflectTest.class.getResourceAsStream("/com/jhon/copy/config.properties");    //在同一个包下    //InputStream is=Re

Java的类加载器

1. Java的类加载器的种类都有哪些? 1.根类加载器(Bootstrap) --C++写的 ,看不到源码 2.扩展类加载器(Extension) --加载位置 :jre\lib\ext中 3.系统(应用)类加载器(System\App) --加载位置 :classpath中 4.自定义加载器(必须继承ClassLoader) 2. 类什么时候被初始化?  1)创建类的实例,也就是new一个对象 2)访问某个类或接口的静态变量,或者对该静态变量赋值 3)调用类的静态方法 4)反射(Class.

java 中类加载器

jar 运行过程和类加载机制有关,而类加载机制又和我们自定义的类加载器有关,现在我们先来了解一下双亲委派模式. java 中类加载器分为三个: BootstrapClassLoader 负责加载 ${JAVA_HOME}/jre/lib 部分 jar 包 ExtClassLoader 加载 ${JAVA_HOME}/jre/lib/ext 下面的 jar 包 AppClassLoader 加载用户自定义 -classpath 或者 Jar 包的 Class-Path 定义的第三方包 类的生命周期

Java:类加载器(ClassLoader)

听上去很高端,其实一般自定义类加载器不需要用户去实现解析的过程,只要负责实现获取类对应的.class字节流部分就ok了,摘录深入理解Java虚拟机的一段话 虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类.实现这个动作的代码模块称为“类加载器” 实现类加载器需要继承ClassLoader这个抽象类,用户只要实现其中的findClass方法即可.在该类的javadoc中给出了这样一个示

JAVA 不同类加载器命名空间的理解

            以前一直有这样一个疑惑: 都说在JAVA中,由不同类加载器加载的类在虚拟机中位于不同的命名空间下,不同命名空间下的类相互不可见. 这让我产生了一个迷惑:如果有一个类A使用了java.util.List类,为什么在运行时会没有错误.因为按照类加载的双亲委派机制,自己写的类A一般由系统类加载器加载,而java.util.List肯定是由启动类加载器(也叫Root类加载器)加载的,所以这两个类应该不在一个命名空间下.那在运行时为什么类A还 是能访问到java.util.List

java高新技术-类加载器

1.类加载器及委托机制的深入分析 > 类加载器的作用:一个java文件中的出现的类,首先要把这个类的字节码加载到内存中,这个类的信息放在硬盘的classPath下的class文件中,  把class文件中的内容加载到内存中去,在进行一些处理,处理完的结果就是字节码,这一系列工作是类加载器在做. > Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:    BootStrap,ExtClassLoader, AppClassLoader > 类加载

java面向对象--类加载器及反射

类加载器 jvm 和 类的关系 当调用 java命令运行一个java程序时,会启动一个java虚拟机进程.同一个jvm的所有线程.所有变量都处于同一个进程里,都使用该jvm进程的内存区. jvm进程终止的情况: 1.程序运行到最后正常结束. 2.遇到System.exit()或Runtime.getRuntime.exit(). 3.遇到未捕获的异常或错误 4.程序所在的平台强制结束了JVM进程 jvm进程终止,jvm内存中的数据将全部丢失. 类加载 当程序主动使用某个类时,如果该类还未被加载到