先从段代码说起
关于异常和this的
- 代码
public class ExceptionTest {
public void test(){
try{
InputStream inputStream = new FileInputStream("test.cpp");
ServerSocket serverSocket = new ServerSocket(3306);
serverSocket.accept();
}catch (FileNotFoundException notFound){
}catch (IOException ioException){
}catch (Exception exception){
}finally{
System.out.println("finally");
}
}
}
- 反编译
在Java代码层面没有参数,但是在字节码层面是有参数的。
隐藏的实例方法参数就是 this
- 对于Java类中的每一个实例方法(非static方法),其在编译后所生成的字节码当中,方法参数的数量总是会比源代码中方法参数的数量多一个(this),它位于方法的第一个参数位置处;这样,我们就可以在Java的实例方法中使用this去访问当前对象的属性以及其他方法。
- 这个操作是在编译期间完成的,即由javac编译器在编译的时候将对this的访问转化为对一个普通实例方法参数的访问,接下来在运行期间,由JVM在调用实例方法时,自动向实例方法传入该this参数。所以,在实例方法的局部变量表中,至少会有一个指向当前对象的局部变量。
再解释max_locals(最大局部变量)
- 该代码max_locals为什么是 4?
- 代码的try中有两个显式定义的局部变量,这就有2个了。
- 再加上隐藏的this,这一共就有3个了。
- 最后,留一个给Exception局部变量,虽然代码中有多个catch,但是,如果有异常的话只有一个catch会被执行。可以理解为这个Exception局部变量会在运行期间等待着被传入catch(类似作为它的参数传入)。这样一共就有4个局部变量了。
异常表
在说异常表之前,先复习一下字节码Code属性的结构
attribute length表示attribute所包含的字节数,不包含attribute_name_index和attribute_length字段。max_stack表示这个方法运行的任何时刻所能达到的操作数栈的最大深度。max_locals表示方法执行期间创建的局部变量的数目,包含用来表示传入的参数的局部变量。code_length表示该方法所包含的字节码的字节数以及具体的指令码。- 具体字节码即是该方法被调用时,虚拟机所执行的字节码。
exception_table这里存放的是处理异常的信息。- 每个
exception_table表项由start_pc,end_pc,handler_pc,catch_type组成。
start_pc和end_pc表示在code数组中的从start_pc到end_pc处(包含start_pc,不包含end pc)的指令抛出的异常会由这个表项来处理。handler_pc表示处理异常的代码的开始处。catch_type表示会被处理的异常类型,它指向常量池里的一个异常类。当catch_type为0时,表示处理所有的异常
阅读字段表
上面的异常表明明有一个Exception最顶层的异常,为啥还多出一个any来表示捕获所有异常?
只能说代码层面Exception是最顶层的,但是在字节码层面就不一样了。any表示哪些连Exception都无法处理的异常。
再者,如果在方法签名上throws异常
- 代码
public void test() throws NullPointerException,FileNotFoundException,IOException{
try{
InputStream inputStream = new FileInputStream("test.cpp");
ServerSocket serverSocket = new ServerSocket(3306);
serverSocket.accept();
}catch (FileNotFoundException notFound){
}catch (IOException ioException){
}catch (Exception exception){
}finally{
System.out.println("finally");
}
}
- 多了点东西
总之,Java字节码对于异常的处理方式:
- 统一采用异常表的方式来对异常进行处理。
- 在jdk 1.4.2之前的版本中,并不是使用异常表的方式来对异常进行处理的,而是采用特定的指令方式。
- 当异常处理存在finally语句块时,现代化的JVM采取的处理方式是将finally语句块的字节码拼接到每一个catch块后面。
- 换句话说,程序中存在多少个catch块,就会在每一个catch块后面重复多少个finally语句块的字节码。