Java类的加载和类加载器
来源:互联网 发布:acdsee pro for mac 编辑:程序博客网 时间:2024/04/29 08:11
关于java类的加载过程,一直弄的不是很懂,最近看了相关资料,来总结一下。
1. 类加载过程
类从被加载到虚拟机内存中开始,到使用之前,要经历加载、连接、初始化,三个阶段。
其中连接包括:验证、准备、解析。
上述顺序只是一个大致的参考,具体实现顺序可能有穿插。
虚拟机规范规定5种场景会触发类的初始化,前面的步骤自然要在初始化之前完成。
加载过程除了可以系统自动完成之外,也可能可以通过ClassLoader类来由程序员控制,加载主要完成:
1. 通过类的全限定名来获取定义此类的二进制字节流
2. 将二进制字节流转化为方法区的运行时数据结构,及Class实例对象。
完成上述过程的是类加载器 .
2. 类加载器
系统默认提供了三种类型的类加载器:
用原生C++实现的启动类加载器bootstrap class loader,其余的都是用java实现的。
引导类加载器(bootstrap class loader):它用来加载 Java 的核心库,是用原生代码来实现的,并不继承自 java.lang.ClassLoader。
扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。
java.lang.ClassLoader是一个抽象类,为我们实现自己的类加载器提供了可能,我们继承它即可。
这里再次摘抄:
ClassLoader 中与加载类相关的方法
- 类加载器的树状组织结构
根据这个结构,类加载器加载类的时候有一个“双亲委派模型”。这个名称我有点不懂,为什么叫“双亲”,明明只是一个父类。也有叫代理模式的,个人觉得这个名字更好一些。 代理模式
当某一个类加载器(也叫初始加载器)在尝试加载某个类的字节码时,会先代理给它的父类加载器,如果父类加载器加载不了,再交由该类加载器加载。
这样做的好处是使java类随着它的类加载器一起具备带有优先级的层次关系,越基础的类越由顶层加载器加载,这样就能保证类的一致性(因为相同类名称的类如果分别由不同的类加载器加载,也认为是两个不同的类,这是通过在Class对象中附带的定义加载器来实现的)。反代理模式的现象
在Java核心类库中定义了一些SPI,翻译过来叫做服务提供接口,具体的实现由第三方来提供,因此我们常常需要在应用中包含第三方提供的jar包,但是不可能都添加到核心库中。因此SPI需要由引导类加载器来加载,但是其中涉及到的调用具体部分,却需要由系统类加载器来加载,但是引导类加载器是不可能让系统类加载器来为其代理的,因此代理模式解决不了。
有线程上下文类加载器可以解决上述问题,还有Class.forName方法。
3. 自定义类加载器
一般通过继承ClassLoader来实现,覆盖其中的某些方法即可。
loadClass(String name)
findClass(String name)
defineClass(String name, byte[] b, int off, int len)
主要涉及到上述三个方法,loadClass中一般包含加载逻辑,比如实现代理逻辑:先尝试代理给父类加载器,如果不成功就调用自己的findClass来加载类,findClass最终将调用defineClass将b中包含的byte数据转换成Class对象。一般为了顺应代理逻辑,推荐覆盖findClass即可。defineClass是final类型的,使用的是原生代码。
小实验:利用自己定义的FileSystemClassLoader加载磁盘上某个地方的class文件。
eclipse工程目录:
package jvmtest;import java.lang.reflect.Method;public class ClassLoaderTest { public static void main(String[] args) throws Exception{ ClassLoader myLoader = new FileSystemClassLoader("d:\\up"); Class<?> class1 = myLoader.loadClass("utils.SayHello"); Object obj1 = class1.newInstance(); Method sayhello = class1.getMethod("hi", null); sayhello.invoke(obj1, null); }}
FileSystemClassLoader是IBM开发社区中的示例,类似的还可以开发从网络中加载的等等。
在调试过程中多次出现java.lang.NoClassDefFoundError: utils/SayHello (wrong name: SayHello)
跟踪了一下,是从defineClass中抛出的,发现字节都已经成功读取到了,但是在defineClass中报错。
后来终于发现是在源文件中没有包含package, 导致defindClass中发现二进制字节流中的信息与输入的类名不一致,导致error。
最后结果:
hi, there!
- Java类的加载和类加载器
- java类加载器的加载顺序
- java中类的加载和加载时机
- 浅谈java类加载过程和类加载器
- Java虚拟机-类加载器和类加载过程
- java 类加载器和类加载方式
- JAVA类加载器和tomcat类加载体系
- Java的类加载器
- Java的类加载器
- Java的类加载器
- JAVA 类的加载器
- Java的类加载器
- Java的类加载器
- java类的加载
- Java类的加载
- Java 类的加载
- Java的类加载
- Java的类加载
- 怎么知道mysql是MyISAM还是InnoDB
- .NET实验6-3
- android 自定义Dialog怎么设置外边背景
- 【UFLDL-exercise5-Softmax Regression】
- 国际化
- Java类的加载和类加载器
- Android中图片处理相关问题
- 伪代码的写法
- SpringMVC源码剖析(二)- DispatcherServlet的前世今生
- 好文要顶
- Spring的设计理念
- 文件上传
- python 的del很强,可以删除类型
- CCF 最优灌溉