拆分复合catch语句为何能消除Java VerifyError?
我来给你把这个问题讲明白!你碰到的这个VerifyError,本质是Java的字节码验证器在检查栈帧类型时,发现了实际类型和预期的栈映射表不匹配,而这个坑正好出在复合catch语句的字节码生成逻辑上。
先看你日志里的核心错误提示:
Type 'org/json/JSONException' (current frame, stack[0]) is not assignable to 'java/lang/RuntimeException' (stack map, stack[0])
简单说就是:字节码验证器发现,当前栈顶的异常是JSONException,但栈映射表(Java用来验证字节码合法性的关键元数据)里标注的这个位置应该是RuntimeException类型——这俩类型不兼容(从错误能看出来JSONException不是RuntimeException的子类,你的DataException应该也不是),所以验证器直接抛出了错误。
那为什么复合catch会搞出这个问题?
当你用catch (DataException | JSONException exception)这种复合捕获时,编译器会尝试把两个异常的处理逻辑合并成一段字节码。但在生成栈映射表的时候,编译器可能犯了个小错误:它把异常处理入口的栈帧类型错误地标记成了RuntimeException,而不是两个捕获异常的共同父类(比如Exception)或者对应具体的异常类型。这就导致验证器检查时,发现实际栈上的异常类型和栈映射表记录的类型不匹配,直接报错。
那拆分catch为什么就好了?
当你拆成两个独立的catch块时,编译器会为每个块生成单独的异常处理逻辑,栈映射表也会准确对应每个catch块要捕获的异常类型:第一个catch块处理DataException,栈映射表就记录此时栈顶是DataException;第二个处理JSONException,栈映射表也会标记正确的类型。字节码验证器检查时,实际类型和预期完全匹配,自然就不会报错了。
这种情况其实是早期Java编译器(或者特定版本编译器)对复合catch语句的字节码生成存在的小bug,拆分catch相当于绕开了这个bug的触发条件,让字节码符合验证器的要求。
备注:内容来源于stack exchange,提问作者Μenelaοs

