1. 引子
java异常简单的用法是在try中检测异常,在catch中进行异常捕获,在finally进行一些资源的释放。这也是最常用的情况。但是,前几天查看帖子
时发现java异常在try、catch、finally三者中有两个以上有返回值(或抛出新异常)时执行顺序就需要进行一下分析了。比如下面的代码:
public static void main(String[] args) {
int testTryCatch3 = testTryCatch3();
System.out.println(testTryCatch3);
}
public static int testTryCatch3(){
try{
System.out.println("at try");
throw new Myexception();
}catch(Exception e){
System.out.println("at catch");
return doSome(2);
}finally{
System.out.println("at finally");
return doSome(3);
}
}
public static int doSome(int i){
System.out.println("at dosome : "+i);
return i;
}
private static class Myexception extends RuntimeException{
public Myexception() {
System.out.println("Myexception constructor");
}
}
本博客主要分析上面的代码的执行顺序,如果你已经知道答案,那么你可以跳过本篇博客
以上为上面代码的执行结果
2. 异常分类
异常继承关系如下图所示
Throwable : 是所有异常的父类,如果使用try{}catch(Throwable e){}则表示任何异常都可以catch到。
受检异常 : Exception中除了RuntimeException以外的异常是受检异常,必须进行try catch处理,比如FilenotFindException、IOException等,编译器在编译时进行校验。个人总结此类异常多为一些即使程序逻辑正常依然可能会出现的异常,比如文件被删除,服务器未打开。
运行时异常 : RuntimeException以及其子类,可以不用try catch处理,比如ArrayIndexOutOfBoundsException、ClassCastException等。个人总结多为一些由于程序设计问题造成的异常。
3. 用法
最基本的用法这儿也就不介绍了。这儿介绍几种本人之前比较疑惑的用法。
- 只有try和finally没有catch 这样做可以让我们既能抛出异常,同时又进行了必要的处理。比如:
public static void testTryCatch2(){
try{
System.out.println("at try");
throw new RuntimeException();
}finally{
System.out.println("\nat finally");
}
}
和一下代码的功能相同
public static void sameTest2(){
try{
System.out.println("at try");
throw new RuntimeException();
}catch(RuntimeException e){
throw e;
}finally{
System.out.println("\nat finally");
}
}
- 利用try catch机制进行代码逻辑设计(第一次看见觉得这种设计很有意思,后来发现这样挺影响性能的)
比如
/**
* 计算x / y的值,如果y为0则输出x
* @return
*/
public static int useTryCatch(int x, int y){
try{
return x/y;
}catch(Exception e){
return x;
}
}
为提高效率可以改为下面代码
public static int correct(int x, int y){
if(y == 0)
return x;
return x/y;
}
- try catch finally中有2个有返回值
public static int testTryCatch4(){
try{
return 1;
}catch(Exception e){
return 2;
}finally{
return 3;
}
}
以上返回结果为3,因为finally会在try或者catch的return与throw之前执行。
类似的情况
public static int testTryCatch4(){
try{
throw new Exception();
}catch(Exception e){
return 2;
}finally{
return 3;
}
}
不难推出返回结果依然为3。再看一例类似的
public static int testTryCatch4(){
try{
throw new Exception();
}catch(Exception e){
return 2;
}finally{
throw new RuntimeException();
}
}
上面代码会抛出异常
- 在try中return finally对返回值进行了修改
public static void main(String[] args) {
System.out.println("at main : "+test());
}
public static int test(){
int i = 0;
try{
System.out.print("at try : " + i);
return ++i;
}catch(Exception e){
System.out.print("at catch : " + i);
}finally{
i += 3;
System.out.println("finally : "+i);
}
return i;
}
把return ++i;分成 ++i;和return i;在++i;时就已经确定了要返回的值了,然后在返回之前执行finally中的代码,最好返回之前的确定值
- 引子中的情况
public static void main(String[] args) {
int testTryCatch3 = testTryCatch3();
System.out.println(testTryCatch3);
}
public static int testTryCatch3(){
try{
System.out.println("at try");
throw new Myexception();
}catch(Exception e){
System.out.println("at catch");
return doSome(2);
}finally{
System.out.println("at finally");
return doSome(3);
}
}
public static int doSome(int i){
System.out.println("at dosome : "+i);
return i;
}
private static class Myexception extends RuntimeException{
public Myexception() {
System.out.println("Myexception constructor");
}
}
1.首先执行System.out.println(“at try”);
2.然后抛出异常进入catch捕获到此异常,执行System.out.println(“at catch”);
3.接着的return doSome(2)可以看成两步,int temp = doSome(2);与return temp;。int temp = doSome(2);正常执行,根据情况3所讲在返回结果之前先执行finally。
4.执行finally中内容,因为finally有返回值或者有throw所以不会回来执行catch 的return。所以testTryCatch3最终返回为3。
以上分析完毕。