类加载器--2
抽象类ClassLoader JVM加载类的过程
----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
说明:这篇日志涉及到的东西我开始也有很多不同的知识。但是幸运的是,我查阅了相关的资料,将难点基本弄清楚了。在这里通过对自己学到的知识和理解,整理出下面这篇日志。不足的地方请大家多多指教!
引用文献1篇:
(1). 《深入理解Java虚拟机:JVM高级特性和最佳实践》 作者:周志明
1. java.lang.ClassLoader抽象类
1). 抽象类ClassLoader基本知识
(1). ClassLoader抽象类的含义
[1]. ClassLoader是负责加载类的类
{1}. 给定一个类名,一个ClassLoader总是尝试确定这个类的位置或者生成可以构成这个类的定义的数据
{2}. {1}的典型实现方法
{2}1. ClassLoader实例将给定的类名转换为字节码文件名
{2}2. ClassLoader实例从本地文件系统读取相应转换的字节码文件
(2). ClassLoader的源码声明
public abstract class ClassLoader {…}
ClassLoader类是抽象类
(3). ClassLoader类的位置
ClassLoader类位于java.lang包
(4). 抽象类ClassLoader的抽象方法
由ClassLoader的源码可知,ClassLoader类中没有任何抽象方法,是一个不含有抽象方法的抽象类。
(2). ClassLoader的常用方法
(1). 将byte[]数组中的数据转化为Class类的实例 -----定义字节码对象
[1]. 源码声明:
protected final Class<?> defineClass(Stringname, byte[] b, int off, int len)
【注意】Java代码中的一个类被使用之前,一定要被正确解析。
[2]. 返回值类型:Class类型实例 Class<?>
【分析】由于返回的任意类型的Class对象,所以加上了通配符?作为类型变量的真实类型。
[3]. 参数类型:
{1}. String name:以字符串形式指定的要加载的类名
{2}. byte[] b + int off +int len:字节数组b中从位置off到off +len -1的数据是用来构建内存中类的数据。
【注意】字节数组b中的通过off和len这两个参数指定的数据段的格式必须符合JVM对class文件的规范。
【Class文件的广义理解】Class文件未必指的是硬盘的某个文件。广义的Class文件指的是一串二进制的字节 (byte)流。
所以这里defineClass方法的参数byte[] b中数据就构成了内存中类数据的来源。
【注意】这个方法在java.lang.ClassLoader中除了被重载的形式调用之外,没有其他的方法对defineClass进行过调用。
(2). 对String形式指定的类名进行加载 -----loadClass(String name)
[1]. 源码声明:
public Class<?> loadClass(String name);
【注意】这个loadClass方法是public的。这个方法实际上是对重载形式loadClass(String, boolean)的封装。
[2]. 返回值类型:Class类型实例 Class<?>
[3]. loadClass()的调用者(API)
这个方法是被JVM进行调用的,不是被Java中的代码进行调用。
(3). 对String形式指定的类名进行加载 ----- loadClass重载形式
[1]. 源码声明:
protected Class<?> loadClass(String name, boolean resolve);
【注意】这个loadClass方法是protected的。这个方法实际是被重载形式loadClass(String)进行调用的。
【注意】这个方法实际上就是JVM内部对“类加载器双亲委托机制的代码表现”****
[2]. 返回值类型:Class类型实例 Class<?>
[3]. 后面详细阐述这个方法的源码。
(4). 寻找指定以String形式出现的类名对应的Class类对象
[1]. 源码声明:
protected Class<?> findClass(String name);
【注意】findClass()方法是protected的。
[2]. 返回值类型:Class类型实例 Class<?>
[3]. API重要说明:
{1}. API已经着重强调:这个findClass()方法应该被遵循“类加载器双亲委派模型”的抽象类ClassLoader的实现子类进行覆盖/重写。
{2}. findClass()方法的调用者就是访问权限是protected的loadClass()方法。
2. JVM加载类的过程
【主要参考】
{1}. 周志明老师的《深入理解Java虚拟机:JVM高级特性和最佳实践》
并进行了整理和组合。
1). JVM进行类加载的基本知识
(1). JVM的类加载机制
[1]. 广义Class文件:Class文件未必指的是硬盘的某个文件。广义的Class文件指的是一串二进制的字节 (byte)流。
[2]. JVM把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被JVM直接使用的Java类。这就是JVM的类加载机制。
(2). Java中的类的生命周期 (简介)
[1]. Java和其他语言的一个不同点
{1}. 以C、C++为例,这些语言是在编译的时候就要进行连接工作的。
{2}. 在Java中类型的加载和连接都是在程序运行期间完成的。
[2]. 运行时进行类型的加载和连接的优缺点:
{1}. 缺点:增加了类加载的开销
{2}. 优点:Java语言动态语言的特性就依赖于这个运行期间的动态加载和动态链接。
【举例】一个使用接口的应用程序可以等到运行时再获取对应的实际实现。
[3]. Java中的类的生命周期
【简单通过图了解一下即可 为类加载做铺垫】
2). JVM进行类加载 (Class Loading) 全过程
【类加载全过程】
就是类在内存中的生命周期的前五步:加载、验证、准备、解析和初始化
(1). 加载 (Loading) 阶段*** -----对理解自定义类加载器很重要
[1]. JVM在加载阶段要做三件事(属于JVM的规范)
{1}. 通过全类名获取定义此类的二进制字节 (byte) 流
{2}. 将这个字节流代表的静态存储结构转化为方法区的运行时数据结构
(知道就好 不用深入)
{3}. 在Java堆中生成一个表达这个类的java.lang.Class类对象,作为{2}中在方法区的数据的访问入口。
[2]. 加载阶段的灵活性
【注意】第一条JVM规范“通过全类名获取定义此类的二进制字节 (byte) 流”并不具体。也就是没有指明从哪里和怎样获取“二进制字节 (byte) 流”,这样就导致许多新技术的出现。
[3]. 由于加载阶段的灵活性出现的新技术举例
{1}. 从zip包中读取,最终成为日后JAR、EAR和WAR格式的基础。【从哪里获取】
{2}. 从网络中获取,此场景的典型应用是Applet。【从哪里获取】
{3}. 运行时通过计算进行获取,此场景的典型应用是动态代理技术。【怎样获取】
在java.lang.reflect.Proxy中,就是使用ProxyGenerator.generateProxyClass来为特定接口生成*$Proxy的代理类二进制字节流
{4}. 由其他文件生成,典型应用场景是JSP。【从哪里获取】
[4]. 类加载 (class loading) 的加载阶段(loading) 的启示和意义
{1}. 相对于类加载过程的其他四个阶段,加载阶段(准确地说是加载阶段获取二进制流的动作)是开发期间可控性最强的阶段。
{2}. 这是因为加载阶段既可以通过系统提供的类加载器来完成,也可以通过自定义类加载器来完成。
{3}. 而此时可以通过自定义的类加载器去控制加载阶段的“字节流获取的具体动作”
【注意】除了在加载阶段用户的应用程序可以通过自定义的类加载器参与进来之外,其余的四个阶段(验证阶段、准备阶段、解析阶段和初始化阶段) 完全由JVM主导和控制,用户的应用程序无法参与!!!!
【结论】如果想在类加载的全过程让自己的应用程序发挥作用的话,只能选择在加载阶段通过自定义类加载器下手
(2). 验证 (Verification) 阶段
【目的】是连接阶段( linking ) 的第一步骤,为了确保Class文件的字节流中含有的信息符合当前JVM的要求并且不会危害JVM自身的安全
(3). 准备 (Preparing) 阶段
[1]. JVM在准备阶段所做的动作
正式为类变量(静态变量)分配内存并为类变量设置初始值的阶段。
[2]. 准备阶段分配内存的具体位置.
由于全部是静态的东西,所以分配内存全部在方法区中进行
[3]. 准备阶段的为类变量设置初始值具体指成员变量的隐式初始化
(4). 解析 (Resolving) 阶段
[1]. JVM在解析阶段所做的动作
JVM将常量池中的符号引用替换为直接引用的过程。
[2].【解释】说白了就是一个翻译的过程
Class文件中的字面常量和哪些底层的JVM指令进行匹配的过程
(5). 初始化 (Initialization)阶段
[1]. JVM在初始化阶段所做的动作
开始真正执行Java程序中定义的代码(或者说是字节码)
[2]. 初始化阶段JVM也要为成员变量进行显示初始化赋值
与准备阶段的隐式初始化赋值不同,初始化阶段对成员变量进行的是显示初始化
----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------