java类加载器总结

类加载机制的加载阶段,是通过类加载器来加载的。

类加载器的模型。 双亲委派, 就是你要加载一个类的,首先会委派给父类的类加载器,看看这个类有没有加载。
不同的类加载器,实现加载不同位置的类。

当一个 JVM 启动的时候,Java 缺省开始使用如下三种类型类装入器:

启动(Bootstrap)类加载器: /lib 下面的类库加载到内存中
标准扩展(Extension)类加载器 它负责将 < Java_Runtime_Home >/lib/ext 或者由系统变量 java.ext.dir 指定位置中的类库加载到内存 开发者可以直接使用标准扩展类加载器
系统(System)类加载器 AppClassLoader 系统类路径(CLASSPATH)中指定的类库加载到内存

通俗的讲,就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载

上面的三种类加载器 是虚拟机默认使用加载器。 每种加载器都默认加载不同位置的类到内存。

tomcat的例子分析:

也就是说,如果你的项目的class文件夹中的类加载 要委托给父类ExtClassLoade加载,继续递归,父类肯定加载不到,因为该class文件并没有在 < Java_Runtime_Home >/lib/ext 这个路径下, 就会返回给当前的类加载器加载,最终加载成功。

如果出现了包重复, 那么就会加载失败。 比如 tomcat的CATALINA_HOME/lib 库 是用一个叫common的加载器加载。如果用户自己库中有servlet-api 。
首先会委派给common WebappX发现了common加载了这个类 就会忽略掉用户库中servlet-api。

tomcat的每个应用都有自己的类加载器。 而普通的java程序 则使用的system类加载器,而tomcat的system则是加载tomcat启动的类。

类加载器与类本身一同确立java虚拟机的唯一性。虽然是这样,双亲委派模型就保证了任意一个类只被一个加载器所加载,也就是说jvm中类的唯一性。而且当你如果要写一个系统的类, 那么委派到顶层类加载器后, 就会被拒绝。因为已经有一个类了。

如果破坏了双亲委派模型的话。就需要自定义类加载器。比如tomcat 能够隔离多个项目的库 就是因为采用了不同的加载器。

几点规则:

Class.forName(String name)默认会使用 调用该代码的类的 类加载器来进行类加载。这种特性,证明了java类加载器中的名称空间是唯一的,不会相互干扰.保证同一个类中所关联的其他类都是由当前类的类加载器所加载的.
如何理解Class.forName(String name)默认会使用调用类的类加载器来进行类加载:
Class caller = Reflection.getCallerClass(3);

当自定义类加载器没有指定父类加载器的情况下,默认的父类加载器即为系统类加载器。即使用户自定义类加载器不指定父类加载器 ,同样是按照三个jvm默认的加载模型来加载。
将父类加载器强制设置为null,无法加载到ext下的库。那么会自动将boot类加载器设置为当前用户自定义类加载器的父类加载器 所以 不管用户 怎么做 jvm都保证 都必须要加载jre环境的库。要不然Object都无法识别了。

有一个前提是 自定义类加载器时 不要修改loadClass的执行逻辑。 而是修改findclass。这才能保证加载到jre环境的库。 这里面的机制没有必要深究。毕竟是人家代码的执行逻辑。

自定义类加载器的方法:

每个ClassLoader都维护了一份自己的名称空间,同一个名称空间里不能出现两个同名的类
不要覆写loadClass(…)方法 因为无法加载jre下类,而是使用findClass

使用线程上下文类加载器,可以在执行线程中,抛弃双亲委派加载链模式,使用线程上下文里的类加载器加载类。
大部分java app服务器(jboss, tomcat..)也是采用contextClassLoader来处理web服务。
线程上下文从根本解决了一般应用不能违背双亲委派模式的问题.
使用线程上下文加载类,也要注意,保证多根需要通信的线程间的类加载器应该是同一个,防止因为不同的类加载器,导致类型转换异常(ClassCastException).

在jdk中, URLClassLoader是配合findClass方法来使用defineClass,可以从网络或硬盘上加载class.
而使用类加载接口,并加上自己的实现逻辑,还可以定制出更多的高级特性.

JVM中一个类用其全名和一个加载类ClassLoader的实例作为唯一标识,不同类加载器加载的类将被置于不同的命名空间。也就是说,加载class文件生成 instanceKlass类型 对象,本来就是在不同命名空间。

osgi 类加载器的机制。实现热部署。我刚做的例子就是自定义一个类加载器去加载class文件。确实和之前的类是不在一个命名空间。 当你从代码中 通过new创建的对象 是当前的类加载器。 而如果要使用自定义类的加载器的对象,只能通过自定义类加载器进行load操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java生命周期包括、验证、准备、解析、初始化和卸六个步骤。阶段,类将字节码读取到内存中;验证阶段,类验证字节码是否完整、有效;准备阶段,类为静态变量分配内存,并将其初始化为默认值;解析阶段,类将符号引用转变为直接引用;初始化阶段,类为类变量赋值;卸阶段,类将类从内存中卸。 ### 回答2: Java类的生命周期可以分为、连接和初始化三个阶段。 第一阶段是,当程序需要使用某个类时,Java虚拟机会先在类路径中寻找该类的字节码文件,并读取到内存中。的过程可分为以下几个步骤: 1. :通过类将类的字节码文件到内存中。 2. 验证:验证字节码文件的合法性,防止安全漏洞。 3. 准备:为静态变量分配内存,并设置默认值。 4. 解析:将符号引用转换为直接引用。 第二阶段是连接,连接阶段主要包括验证、准备和解析三个步骤: 1. 验证:再次验证字节码文件的合法性,检查类与类之间的引用是否正确。 2. 准备:为静态变量分配内存,并设置默认值。 3. 解析:将符号引用转换为直接引用,将静态方法的调用转换为实际的内存地址。 第三阶段是初始化,当类被初次使用时才会触发初始化,初始化阶段主要进行静态变量赋值和静态代码块的执行等操作。初始化的过程是按照声明的顺序由上至下执行的,可以通过静态代码块来执行一些静态属性的初始化操作,也可以在此进行一些必要的资源以及与外部系统的交互等操作。 在Java中,类的是动态的,即在运行时根据需要,并且类的采用了双亲委派机制,即类类时会先委托给父类,只有在父类找不到所需类的情况下才由自己来总结起来,Java类的生命周期包括、连接和初始化三个阶段,其中阶段负责将类的字节码文件到内存中,连接阶段负责验证、准备和解析相关的操作,初始化阶段进行静态变量赋值和静态代码块的执行等操作。 ### 回答3: Java类的生命周期可以分为(Loading)、链接(Linking)和初始化(Initialization)三个阶段。 阶段: 在阶段,虚拟机通过类(ClassLoader)将字节码文件到内存中,并为类创建一个Class对象(在方法区中),用来封装类的各种信息。 链接阶段: 链接阶段又可以分为验证(Verification)、准备(Preparation)和解析(Resolution)三个步骤。 1. 验证:验证阶段主要是对字节码进行各种静态的、安全性的验证操作,比如格式验证、语义验证等,确保类的字节码是合法且安全的。 2. 准备:准备阶段主要是对类的静态成员(静态变量和常量)进行内存分配,并设置默认值。 3. 解析:解析阶段是将类、方法、字段等符号引用转化为直接引用的过程。 初始化阶段: 在初始化阶段,虚拟机对类进行初始化操作,包括静态变量的赋值、静态代码块的执行等。初始化阶段是类过程中的最后一步,只有在必要的时候才会进行。 需要注意的是,类的初始化是一个被动过程,只有在首次使用类的时候才会触发初始化操作,比如创建对象、访问静态变量或者静态方法等。 总结起来,Java生命周期包括阶段、链接阶段和初始化阶段。阶段是将字节码文件到内存,并创建Class对象;链接阶段是验证、准备和解析的过程;初始化阶段是对类进行初始化操作,只有在使用类的时候才会触发初始化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值