免费预览已结束,剩余1页可下载查看
下载本文档
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1. Java类加载机制1.1 类加载基础1.1.1 类加载器JVM在初期是将.java文件,编译成.class文件,当程序运行的时候,Java 虚拟机就将编译生成的 . class 文件按照需求和一定的规则加载进内存,组织成为一个完整的 Java 应用程序,JVM会把每个单独的类和接口编译成一个单独的.class文件,这些文件对于 Java 运行环境来说就是一个个可以动态加载的单元。我们可以在不重新编译其它代码的情况下,只编译需要修改的单元,并把修改文件编译后的 . class 文件放到 Java 的路径当中, 等到下次该 Java 虚拟机器重新激活时,这个逻辑上的 Java 应用程序就会因为加载了新修改的 .class 文件,自己的功能也做了更新,这就是 Java 的动态性。Java只有在需要的时候才会把类加载(load)进来。而类的加载是通过类加载器(Class Loader)来完成的。通常程序启动之后,会用到三个类加载器:BootstrapLoader、ExtClassLoader、AppClassLoader。(1) 启动(Bootstrap)类加载器:BootstrapLoader(C+编写的类)负责搜索环境变量sun.boot.class.path指定的目录,即JRE(如果装了JDK,那么就是JDK下的JRE文件夹)所在目录下的classes目录或者lib目录下的jar文件,并将指定的类加载到虚拟机;(2) 标准扩展(Extension)类加载器:ExtClassLoader负责搜索环境变量java.ext.dirs指定的目录,也就是JRE所在目录的lib/ext目录下的classes目录或者jar文件,并将指定的类加载到虚拟机;(3) 系统(System)类加载器:AppClassLoader负责搜索环境变量java.class.path指定的目录,也就是我们在环境变量中设置的CLASS_PATH目录下的类或者jar文件,并将指定的类加载到虚拟机。注意,ExtClassLoader和AppClassLoader是.URL.ClassLoader的子类,不过这两个子类定义在sun.misc.Launcher类中,是Launcer的内部类。另外,还有一种类加载器:线程上下文类加载器(context class loader)线程上下文类加载器是从 JDK 1.2 开始引入的。通过类 java.lang.Thread 的getContextClassLoader() 和 setContextClassLoader(ClassLoader cl) 方法可以获取和设置线程的上下文类加载器。如果没有通过 setContextClassLoader(ClassLoader cl) 方法进行设置的话,线程将继承其父线程的上下文类加载器。Java 应用运行的初始线程的上下文类加载器是系统类加载器。在线程中运行的代码可以通过此类加载器来加载类和资源。前面提到的类加载器的代理模式并不能解决 Java 应用开发中会遇到的类加载器的全部问题。Java 提供了很多服务提供者接口(Service Provider Interface,SPI),允许第三方为这些接口提供实现。常见的 SPI 有 JDBC、JCE、JNDI、JAXP 和 JBI 等。这些 SPI 的接口由 Java 核心库来提供,如 JAXP 的 SPI 接口定义包含在 javax.xml.parsers 包中。这些 SPI 的实现代码很可能是作为 Java 应用所依赖的 jar 包被包含进来,可以通过类路径(CLASSPATH)来找到,如实现了 JAXP SPI 的 Apache Xerces 所包含的 jar 包。SPI 接口中的代码经常需要加载具体的实现类。如 JAXP 中的 javax.xml.parsers.DocumentBuilderFactory 类中的 newInstance() 方法用来生成一个新的 DocumentBuilderFactory 的实例。这里的实例的真正的类是继承自 javax.xml.parsers.DocumentBuilderFactory ,由 SPI 的实现所提供的。如在 Apache Xerces 中,实现的类是 org.apache.xerces.jaxp.DocumentBuilderFactoryImpl 。而问题在于,SPI 的接口是 Java 核心库的一部分,是由引导类加载器来加载的;SPI 实现的 Java 类一般是由系统类加载器来加载的。引导类加载器是无法找到 SPI 的实现类的,因为它只加载 Java 的核心库。它也不能代理给系统类加载器,因为它是系统类加载器的祖先类加载器。也就是说,类加载器的代理模式无法解决这个问题。线程上下文类加载器正好解决了这个问题。如果不做任何的设置,Java 应用的线程的上下文类加载器默认就是系统上下文类加载器。在 SPI 接口的代码中使用线程上下文类加载器,就可以成功的加载到 SPI 实现的类。线程上下文类加载器在很多 SPI 的实现中都会用到。1.1.2 Java类加载器层次关系虚拟机启动的时候,会载入BootstrapLoader,然后BootstrapLoader会将ExtClassLoader加载进来,并且将ExtClassLoader的父类装载器设置成null,用以表示其父类加载器是BootstrapLoader;最后BootstrapLoader将AppClassLoader加载进来,并且将AppClassLoader的父类装载器设置成ExtClassLoader。这里的父类装载器并不表示继承关系,表示的是装载它的加载器,只是为了说明层次关系。Launcher$ExtClassLoader.class 与 Launcher$AppClassLoader.class (这样写是因为ExtClassLoader和AppClassLoader是Launcher的内部类)都是由 Bootstrap Loader 所加载,所以类加载器的父加载器和由哪个类加载器加载没有关系。需要注意的是:我们可以使用getClassLoader()方法来获取一个类的真实加载器,并且可以调用parent()方法来得到它的父类加载器,如果返回的应该是BootstrapLoader,那么返回的对象实际是null,因为BootstrapLoader是由C+编写的类,无法通过Java程序获得,所以在加载ExtClassLoader的时候,强制设定了它的父类加载器为null。1.1.3 委托类加载在Java的类加载器中,存在一种委托关系:当类加载器需要加载一个类的时候,会首先委托它的父类加载器从其搜索路径中搜索相关类,如果找到,则加载父类加载器所找到的类,否则,才从自身的搜索路径中寻找相关的类,如果还是找不到,将会抛出一个NoClassDefFoundError异常。需要注意,这种委托关系是递归的,会一层一层向上委托,直到BootstrapLoader。1.2 Java类加载方式1.2.1 预先加载与按需求加载Java 运行环境为了优化系统,提高程序的执行速度,在 JRE 开始运行时会将 Java 运行所需要的基本类采用预先加载( pre-loading )的方法全部加载要内存当中,因为这些单元在 Java 程序运行的过程当中经常要使用的,主要包括 JRE 的 rt.jar 文件里面所有的 .class 文件。当 java.exe 虚拟机开始运行以后,它会找到安装在机器上的 JRE 环境,然后把控制权交给 JRE , JRE 的类加载器会将 lib 目录下的 rt.jar 基础类别文件库加载进内存,这些文件是 Java 程序执行所必须的,所以系统在开始就将这些文件加载,避免以后的多次 IO 操作,从而提高程序执行效率。我们在程序中需要使用自己定义的类的时候就要使用按需求加载方法( load-on-demand ),就是在 Java 程序需要用到的时候再加载,以减少内存的消耗。1.2.2 隐式加载和显式加载 隐式:程序中用 new 关键字来定义一个实例变量, JRE 在执行到 new 关键字的时候就会把对应的实例类加载进入内存。JRE 系统在后台自动的帮助用户加载,减少了用户的工作量,也增加了系统的安全性和程序的可读性。 显式:程序员自己写程序把需要的类加载到内存当中 (1) 通过java.lang.Class的forName()方法Class class = Class.forName(Test); /Test 类为自定义的一个测试类;Test test = (Test)class.newInstance 我们通过 Class 类的 forName (String str) 方法把自定义类 Test 加载进来,并通过 newInstance()方法把实例初始化。(2) 通过java.lang.ClassLoader的loadClass()方法先得到一个相应的ClassLoader,然后通过这个ClassLoader对象的loadClass(String name)方法来加载其他类。获取到ClassLoader的方式:1) Test t=new Test();ClassLoader loader=t.getClass().getClassLoader();2) Class c=Test.class;ClassLoader loader=c.getClassLoader();接下来,我们就可以通过loader.loadClass(“TestLoad”)来加载TestLoad类。1.2.3 自定义类加载机制除了使用上述三种加载类以外,还可以使用.URLClassLoader来自己定义一个新的类加载机制,由自己来控制类加载的细节。这种情况下,它默认的父类加载器为AppClassLoader,如果要改变,则需要自己指定。URL url = new URL(file:/d:/test/lib/); URLClassLoader urlCL = new URLClassLoader(new URLurl); Class class = urlCL.loadClass(Test);Test test = (Test)class.newInstance();首先定义 URL 指定类加载器从何处加载类, URL 可以指向网际网络上的任何位置,也可以指向我们计算机里的文件系统 ( 包含 JAR 文件 )。上述范例当中我们从 file:/d:/test/lib/ 处寻找类;然后定义 URLClassLoader 来加载所需的类,最后即可使用该实例了。1.3 Java类加载分析1.3.1 加载过程及方式系统类加载器(AppClassLoader)调用ClassLoader(ClassLoader parent)构造函数将父类加载器设置为标准扩展类加载器(ExtClassLoader)。扩展类加载器(ExtClassLoader)调用ClassLoader(ClassLoader parent)构造函数将父类加载器设置为null。因为如果父加载器为null,则会调用本地方法进行启动类加载尝试。那么对于ExtClassLoader来说,在进行向上委托的过程中,因为它的父加载器为空,所以此时就会通过调用本地方法检查是否是由BootstrapLoader加载的类。1.3.2 注意AppClassLoader 与ExtClassLoader 在整个虚拟机之中只会存有一份,一旦建立了,其内部所参考的搜寻路径将不再改变,也就是说,即使我们在程序里利用System.setProperty() 来改变系统参数的内容,仍然无法更动AppClassLoader 与ExtClassLoader 的搜寻路径。因此,执行时期动态更改搜寻路径的设定是不可能的事情。1.4 相关知识点1.4.1 JVM如何判断两个类是否相同JVM不仅要看类的全名是否相同,还要看加载此类的类加载器是否一样。只有两者都相同的情况,才认为两个类是相同的。即便是同样的字节代码,被不同的类加载器加载之后所得到的类,也是不同的。1.4.2 代理模式的动机代理模式是为了保证 Java 核心库的类型安全。所有 Java 应用都至少需要引用 java.lang.Object 类,也就是说在运行的时候,java.lang.Object 这个类需要被加载到 Java 虚拟机中。如果
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 购买衣服购销合同范本
- 物业灯带维修合同范本
- 茶叶打包出售合同范本
- 物业工程养护合同范本
- 酒店保洁务工合同范本
- 物业地砖维护合同范本
- 2025年木工几何力学试卷及答案
- 物业维修安装合同范本
- 直播带货招聘合同范本
- 药厂采购白糖合同范本
- GB/T 23595.2-2025LED用稀土荧光粉试验方法第2部分:相对亮度的测定
- 《动物细胞结构与功能》课件
- 中国围棋历史规则模具介绍《围棋入门基础知识介绍》课件
- DBJ51-T 040-2021 四川省工程建设项目招标代理操作规程
- 《酒店客户关系管理 》课件-项目八 酒店客户关系数字化运营
- 首都经济贸易大学《法理学》2021-2022学年第一学期期末试卷
- 实验11 探究通电螺线管外部磁场的方向
- 仓库保管承诺保证书
- 六年级《水果拼盘》名师公开课获奖课件百校联赛一等奖课件
- 创新方法大赛理论知识考核试题题库及答案
- 激光熔覆技术优化
评论
0/150
提交评论