Java@深入理解Java虚拟机:类加载机制( 三 )


  • 如果通过了第一步 , 在类C中查找是否有简单名称和描述符都与目标想匹配的方法 , 如果有则返回这个方法的直接引用 , 查找结束 。
  • 否则 , 在类C的父类中递归查找是否有简单名称和描述符都与目标相匹配的方法 , 如果有则返回这个方法的直接引用 , 查找结束 。
  • 否则 , 在类C实现的接口列表及它们的父接口之中递归查找是否有简单名称和描述符都与目标相匹配的方法 , 如果存在匹配的方法 , 说明类C是一个抽象类 , 这时候查找结束 , 抛出java.lang.AbstractMethodError异常 。
  • 否则 , 宣告方法查找失败 , 抛出java.lang.NoSuchMethodError 。
    1. 接口方法解析
      1. 与类的方法解析相反 , 如果在接口方法表中发现class_index中的索引C是个类而不是接口 , 那么就直接抛出java.lang.IncomepatibleClassChangeError异常 。
      2. 否则 , 在接口C中查找是否有简单名称和描述符都与目标相匹配的方法 , 如果有则返回这个方法的直接引用 , 查找结束
      3. 否则 , 在接口C的父接口中递归查找 , 直到java.lang.Object类(接口方法的查找范围也会包括Object类中的方法)为止 , 看是否有简单名称和描述符都与目标相匹配的方法 , 如果有则返回这个方法的直接引用 , 查找结束 。
      4. 对于规则3 , 由于Java的接口允许多重继承 , 如果C的不同父接口中存有多个简单名称和描述符都与目标相匹配的方法 , 那将会从这多个方法中返回其中一个兵结束查找 。
      5. 否则 , 宣告方法查找失败 , 抛出java.lang.NoSuchMethodError异常 。
    5、初始化
    • 根据程序员通过程序编码指定的主观计划去初始化类变量和其他资源 。 (初始化阶段就是执行类构造器<clinit>()方法的过程) 。
    • <clinit>()方法时有编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{块)中的语句合并产生的 , 编译器收集的顺序是由语句在源文件中出现的顺序决定的 , 静态语句块中只能访问到定义在静态语句块之前的变量 , 定义在它之后的变量 , 在前面的静态语句块可以赋值 , 但是不能访问 。

    • <clinit>()方法与类的构造函数(即在虚拟机视角中的实例构造器<init>()方法)不同 , 它不需要显式地调用父类构造器 , Java虚拟机会保证在子类的<clinit>()方法执行前 , 父类的<clinit>()方法已经执行完毕 。 因此在Java虚拟机中第一个被执行的<clinit>()方法的类型肯定是java.lang.Object 。
    • 由于父类的<clinit>()先执行 , 也就意味着父类中定义的静态语句块要优先于子类的变量赋值操作 。