第二章(四) Java虚拟机结构-特殊方法、异常

2.9 特殊方法
    对于Java虚拟机,每一个用java语言编写的构造函数作为实例初始化方法都有一个特殊的名字:<init>。这个名字是编译器提供的。因为<init>不是一个正确的方法标识符,所以它是不能直接在Java编程语言中使用的。实例初始化方法只能通过Java虚拟机的invokespecial指令调用,并且只能用在未被初始化的类实例。关于实例初始化方法的访问权限可以参考Java语言规范的6.6章。
    一个类或接口最多含有一个类或接口初始化方法,并且通过调用这个方法来初始化。类或接口的初始化方法也有一个特殊的名字<clinit>,此方法没有参数,并且是void的。
    其他的在类文件中被命名成<clinit>的方法都是毫无影响的,它们不是类或接口的初始化方法,它们也不会被任何Java虚拟机指令调用,也不会被Java虚拟机自己调用
    在版本号是51.0或以上的类文件中,类或接口的初始化方法还会有额外的ACC_STATIC标志。
    这个要求是在Java SE 7中引入的。在版本号是50.0或以下的类文件中,一个void的不带有任何参数的<clinit>方法就会被认为是初始化方法,不管这个方法是不是有ACC_STATIC标志
    <clinit>方法名是有编译器提供的,因为<clinit>不是一个正确的方法标识符,所以它是不能直接在Java编程语言中使用的。类和接口的初始化方法是被Java虚拟机隐式调用的,它不能直接被任何的Java虚拟机指令调用,只能作为类初始化处理的一部分被间接调用。
    一个方法只有全部满足下面的条件才是签名多态的
   
  • 在java.lang.invoke.MethodHandle类中声明
  • 只有一个正式的Object[]类型的参数
  • 有一个Object类型的返回值
  • 类文件中对应的方法有ACC_VARARGS 和 ACC_NATIVE标识

    在Java SE 8,唯一的签名多态的方法是java.lang.invoke.MethodHandle中的invoke和invokeExact方法
    Java虚拟机为了有效的调用method handle,在用invokevirtual指令调用签名多态方法时会特殊对待。一个method handle是一个强类型的,是对一个方法、构造器、字段或者相同级别操作的直接的可执行引用,拥有随意转换的参数和返回值。这些转换是非常普通的,包含转换、插入、删除、置换等模式。可以参考java.lang.invoke包的API获取更多信息。
2.10 异常
    在Java虚拟机中一个异常是由Throwable类或是它的子类的一个实例表示的。抛出一个异常会从抛出异常的地方立即引起一个非局部的控制转换。
    许多异常是伴随着它所在的线程中的一个动作而同步出现的。但是一个潜在的异步异常,可出现在一个程序的任何地方。Java虚拟机抛出异常的原因有一下三个:
   
  • 一个athrow指令被执行
  • 一个不正常的执行条件被Java虚拟机同步的检测到。这些异常不会在程序中一个不确定的被抛出,只能同步的出现在如下的一些指令后:
  •             - 指定异常作为一个可能的结果,例如:
                    > 当这个指令具体的操作违反了Java编程语言的语法,例如数组越界。
                    > 当一个错误出现在程序的加载和链接部分
                - 引起一些限制资源的溢出,例如需要太多的内存
  • 因为如下原因出现异步异常
  •             - Thread或ThreadGroup的stop方法被调用
                - Java虚拟机实现的内部错误发生
                  stop方法可能被一个想影响另一个线程或者是想影响在一个特定线程组里所有线程的线程调用。因为它们能出现在其他线程执行过程的任何地方,所以是异步的。一个虚拟机的内部错误也被认为是异步的。

    Java虚拟机允许很小数量的操作在异步异常被抛出之前执行, 这种延迟允许优化的代码处理发现并且抛出这些异常,当这些代码遵守Java编程语言语法,并且实际上需要处理这些异常的地方。
    Java虚拟机抛出异常时精确地;当控制发生转化时,所有在抛出异常时点之前执行的指令必须已经发生。任何在抛出异常之后的指令都不能被执行。如果优化的代码推测性的执行了一些在抛出异常点之后的指令,对于这个程序从用户可见的角度,这些代码必须隐藏这些推测执行的效果。
    在Java虚拟机中每一个方法都会和0个或多个异常处理器关联在一起。一个异常处理器指定了它能处理的代码范围,描述了它能处理的异常的类型,指定了处理相应异常的代码的位置。一个异常能匹配上一个异常处理器,仅当引起异常的指令在此异常处理器处理的代码范围内,并且异常类型和此异常处理器能处理的异常时同一个类,或是此类的子类。当一个异常抛出时,Java虚拟机会在当前方法中查找相匹配的异常处理器,如果能找到,系统就转而执行这个异常处理器指定的异常处理代码。如果在当前方法中没有找到对应的异常处理器,当前方法就会异常结束。当方法异常结束时,当前方法对应的操作数栈和本地变量表被丢弃,对应的栈帧被取出,调用此方法的方法的栈帧被重新加载。对应的异常在调用者的栈帧上下文中被重新抛出,并在方法调用链上如此持续进行。如果在到达方法调用链的最上层时扔没有合适的异常处理器找到,那么抛出这个异常的线程就会被终止。
    一个方法对应的异常处理器被查找的顺序是非常重要的。在一个类文件中,一个方法的异常处理器存储在一个表中。在运行时,当一个异常被抛出,Java虚拟机按照当前方法对应的异常处理器表中的顺序从第一个开始依次查找。
    注意,Java虚拟机并不强制嵌套或排序一个方法对应异常处理器表。Java程序语言中异常处理的语法仅仅通过编译器完成。如果类文件通过其他的方式生成,定义的查找过程需要确保所有Java虚拟机的实现表现出相同的行为。

猜你喜欢

转载自fengyilin.iteye.com/blog/2273451