类的加载过程值准备与解析(九)

准备阶段

准备阶段是正式为类中定义的变量(静态变量,被static修饰的变量)分配内存并设置类变量初始值的阶段,不包括实例变量。

public static int value = 123;
此时只会将value赋值为初始值0.真正赋值为123是在类构造器 < clinit>() 方法中。


public static final int value = 123;
准备阶段就会将value赋值为123

解析

解析阶段是Java虚拟机将常量池内的符号引用替换为直接引用的过程。

符号引用

符号引用用一组符号来描述所引用的目标,符号可以是任何形式的字面量,引用目标不一定是虚拟机内存之内的,符号引用的字面量形式明确规定在Class文件中。

直接引用

直接引用是可以直接指向目标的指针、相对偏移量、或者是一个能间接定位到目标的句柄,引用目标一定在虚拟机中存在。

解析动作主要针对类或接口、字段、类方法、接口方法。方法类型、方法句柄和调用点限定符这7类符号引用进行

类或接口的解析

假设当前所处的类为D,如果要把一个未解析过的符号引用N解析为一个类或接口C的直接引用,虚拟机完程整个解析过程需要三个步骤:

  1. 如果C不是一个数组类型,虚拟机会把代表N的全限定名传给D的类加载器去加载C。加载过程中可能触发其他类的加载动作。
  2. 如果C是一个数组类型,并且数组加载元素类型为对象,加载数组元素类型。
  3. 如果上面两步没有出现任何异常,那么C在虚拟机中实际成为了一个有效的类或者接口了,但完成解析之前还要进行符号验证,确认D是否具备C的访问权限。如果不具备权限,则会抛出 java.lang.IllegalAccessError 异常。

JDK9引入了模块化后,还要检查模块间的访问权限
如果D拥有C的访问权限,那就意味着下面三条至少有一条成立:

  1. 被访问类C是public的,并且与访问类D处于同一个模块。
  2. 被访问类C是public的,不与访问类D处于同一个模块,但被访问类C的模块允许被访问类D的模块进行访问。
  3. 被访问类C不是public的,但它与访问类D处于同一个包。

字段解析

要解析一个未被解析过的字段符号引用,首先将会对字段表内class_index项中的CONSTANT_Class_info符号引用进行解析。
解析完成要对这个类或者接口C进行后续字段的搜索:

  1. 如果C本身就包含了简单名称和字段描述符都与目标相匹配的字段,则返回这个字段的直接引用,查找结束。
  2. 否则,如果C中实现了接口,则会按照继承关系,递归的搜索各个接口,如果接口中包含了简单名称和字段描述符都与目标相匹配的字段,则返回这个字段的直接引用,查找结束。
  3. 否则,如果C不是 java.lang.Object 的话,就会按照递归关系从下向上的搜索父类,如果父类中包含了简单名称和字段描述符都与目标相匹配的字段,则返回这个字段的直接引用,查找结束。
  4. 否则,查找失败,抛出 java.lang.NoSuchFieldError 异常。
    如果成果的放回了引用,则会对其进行权限验证。

方法解析

先解析出方法表的class_index项中索引方法所属的类或者接口的符号引用,如果解析成功,用C代表这个类,接下来虚拟机会按照如下步骤进行搜索:

  1. 由于Class文件格式中类的方法和接口的方法符号引用的常量类型是分开的,如果发现C是一个接口的话就抛出异常 java.lang.IncompatibleClassChangeError。
  2. 在C中查找包含了简单名称和字段描述符都与目标相匹配的方法,如果有则返回这个方法的直接引用,查找结束。
  3. 否则,在类C的父类中递归查找包含了简单名称和字段描述符都与目标相匹配的方法,如果有则返回这个方法的直接引用,查找结束。
  4. 否则,在类C的实现接口列表及他们的父类接口之中递归查找是否有包含了简单名称和字段描述符都与目标相匹配的方法,如果有则说明C是一个抽象类,这是查找结束抛出异常 java.lang.AbstractMethodError。
  5. 否则,方法查找失败,抛出 java.lang.NoSuchMethodError。
    最后,如果查找成功并返回了直接引用,就对这个方法进行权限认证。

接口方法解析

先解析出接口方发表的class_index项中索引方法所属的类或者接口的符号引用,如果解析成功,用C代表这个接口,接下来虚拟机会按照如下步骤进行搜索:

  1. 与方法解析相反,如果是一个类的话就抛出异常。
  2. 否则,在接口C中查找包含了简单名称和字段描述符都与目标相匹配的方法,如果有则返回这个方法的直接引用,查找结束。
  3. 否则,在其父接口中递归查找,直到找到 java.lang.Object类为止,看到是否有简单名称和字段描述符都与目标相匹配的方法,如果有则返回这个方法的直接引用,查找结束。
  4. 由于允许Java接口的多继承,如果不同父类中存在简单名称和字段描述符都与目标相匹配的方法,则会放回其中一个。
  5. 否则,查找失败。

猜你喜欢

转载自blog.csdn.net/weixin_43663421/article/details/109289643
今日推荐