我们来看一个例子,关于java异常处理(exception handling)的一些知识点。
看下面这段程序。方法pleasethrow接受一个exception的实例,然后简单地将该实例抛出。然后调用这个方法时,我传入了一个sqlexception的实例。因为pleasethrow的调用包裹在一个try catch块里,
问题:plessethrow方法抛出的sqlexception可以成功被catch住么?
public class exceptionforquiz<t extends exception> { private void pleasethrow(final exception t) throws t { throw (t) t; } public static void main(final string[] args) { try { new exceptionforquiz<runtimeexception>().pleasethrow(new sqlexception()); } catch( final sqlexception ex){ system.out.println(jerry print); ex.printstacktrace(); }}}
答案:上面这段代码有语法错误,不能通过编译!
我们来一步步分析。
java类exceptionforquiz<t extends exception>使用了一个泛型语法,t extends exception意思是这个泛型类实例化的时候,传入的类型参数t必须是exception以及它的子类。
我在实例化类exceptionforquiz时,传入的类型参数是runtimeexception。
runtimeexception在java里是一种unchecked异常,即使一个方法运行时可能会抛出runtimeexception,也不需要开发人员在方法前用代码显式声明。
看jdk runtimeexception的注释说的很清楚:unchecked exceptions do not need to be declared in a method or constructor's clause if they can be thrown by the execution of the method or constructor.
这个作者frank yellin一定是个大牛。
因为泛型是 java 1.5 版本才引进的概念,关于泛型有一个类型擦除的概念,即泛型信息只存在于代码编译阶段,编译之后的代码里,与泛型相关的信息会被擦除掉。比如之前泛型类中的类型参数部分如果没有指定上限,像这种写法<t>, 则会被转译成普通的object类型。如果指定了上限如<t extends string>则类型参数就被替换成类型上限。
为了简化起见,我们先把代码里的try catch块去掉。
下面是从exceptionforquiz.class反编译之后的代码:
我们从上图能观察到,方法pleasethrow和雷exceptionforquiz的泛型参数runtimeexception已经被擦除掉了。pleasethrow这个方法能抛出的异常类型已经被擦除成为exception了。
使用javap观察编译生成的字节码,同样能发现类型参数runtimeexception被擦除的事实:
看第二个红色高亮区域:exceptions: throw java.lang.exception
现在我们来看编译器会报什么错误消息:unreachable catch block for sqlexception. this exception is never thrown from the try statement body.
根据异常类型擦除的事实,这个错误消息是合理的,因为pleasethrow方法的声明现在只能抛出类型为exception的异常,所以第14行的catch永远也没有办法接收到类型为sqlexception的异常,所以编译器抛出错误。
如何消除掉这个编译器错误呢?把第14行的sqlexception改成runtimeexception即可。
但是这样的话,虽然消除了语法错误,但是方法pleasethrow抛出的sqlexception没有办法被catch住,会报运行时错误:
如何把pleasethrow抛出的sqlexception也用catch语句接住呢?将第14行的runtimeexception改成所有异常的超类:exception。
再次执行,这次既没有语法错误,也没有运行时错误了:sqlexception已经成功地被第14行的catch语句捕捉住了。
以上就是java异常处理的详细介绍的详细内容。