Java异常处理:如何写出“正确”但被编译器认为有语法错误的程序
摘要:文章的标题看似自相矛盾。
本文分享自华为云社区《Java异常处理:如何写出“正确”但被编译器认为有语法错误的程序》,作者: Jerry Wang 。
文章的标题看似自相矛盾,然而我在“正确”二字上打了引号。我们来看一个例子,关于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>使用了一个泛型语法,Textends 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块去掉。
![](https://img-blog.csdnimg.cn/img_convert/eac69b52ba17f5bd9f373dd481d85aad.png)
下面是从ExceptionForQuiz.class反编译之后的代码:
![](https://img-blog.csdnimg.cn/img_convert/bd0dbb8bb200da628135ed78ebb46d27.png)
我们从上图能观察到,方法pleaseThrow和雷ExceptionForQuiz的泛型参数RuntimeException已经被擦除掉了。pleaseThrow这个方法能抛出的异常类型已经被擦除成为Exception了。
使用javap观察编译生成的字节码,同样能发现类型参数RuntimeException被擦除的事实:
看第二个红色高亮区域:Exceptions: throw java.lang.Exception
![](https://img-blog.csdnimg.cn/img_convert/ed5c109f66206e8a46d7bea09444887c.png)
现在我们来看编译器会报什么错误消息:Unreachable catch block for SQLException. This exception is never thrown from the try statement body.
![](https://img-blog.csdnimg.cn/img_convert/f3e1201c2c89b84564eea7b3b03862a5.png)
根据异常类型擦除的事实,这个错误消息是合理的,因为pleaseThrow方法的声明现在只能抛出类型为Exception的异常,所以第14行的catch永远也没有办法接收到类型为SQLException的异常,所以编译器抛出错误。
如何消除掉这个编译器错误呢?把第14行的SQLException改成RuntimeException即可。
但是这样的话,虽然消除了语法错误,但是方法pleaseThrow抛出的SQLException没有办法被catch住,会报运行时错误:
![](https://img-blog.csdnimg.cn/img_convert/c525bf07e286e6e286f77b9453d8773a.png)
如何把pleaseThrow抛出的SQLException也用catch语句接住呢?将第14行的RuntimeException改成所有异常的超类:Exception。
再次执行,这次既没有语法错误,也没有运行时错误了:SQLException已经成功地被第14行的catch语句捕捉住了。
![](https://img-blog.csdnimg.cn/img_convert/81af7a87661a3ac227f442e3e4e22701.png)
相关文章
- Java 异常错误 (Ljava/lang/String;)L java/lang/String;「建议收藏」
- java源程序文件扩展名_JAVA源代码的扩展名为( )
- java启动器_JAVA基础:Java 启动器如何查找类
- MySQL字段类型如何转为java_Java JDBC中,MySQL字段类型到JAVA类型的转换
- eclipse运行java程序_如何在Eclipse中运行简单的Java程序?「建议收藏」
- java记录异常日志要遵循那些规则详解编程语言
- 实现Java程序操作MySQL数据库(java调用mysql)
- Linux环境下编译Java程序的指南(linux编译java)
- 程序Linux下构建Java应用:从入门到精通(linux下开发java)
- Java程序更新MySQL记录的实践(java更新mysql)
- Oracle与Java的连接:揭秘数据库开发技术之间的神奇联系(oracle连接java)
- 解析Java中的Linux路径(java中linux路径)
- Java程序如何在Linux上顺利部署?快来了解一下!(java部署到Linux)
- Java程序在Linux系统中实现命令操作(java运行linux命令)
- java程序中的延时加载异常及解决方案
- Java进阶教程之异常处理