Java谈谈throws和try catch的几种情况的后续代码会不会执行

背景

下面均以ParseException(解析异常)为例叙述。

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class MyException {
    public static void main(String[] args){
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
        Date date = sdf.parse("2021-0829");
        System.out.println("执行后续代码...");
    }
}

这种情况下,那么 parse() 会报错,因为这里要防止ParseException,它属于编译异常,必须用throws或者try…catch解决。

方式一:throws

我们这里先用throws:

在parse处Alt+Enter,选择Add exception to method signature,会自动在main后面添加throws ParseException。代码变为:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class MyException {
    public static void main(String[] args) throws ParseException {
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
        Date date = sdf.parse("2021-0829");
        System.out.println("执行后续代码...");
    }
}

用throws这样写,就是把异常交给JVM(java虚拟机)处理。这样做的缺点是会造成:

控制台直接显示异常信息,并终止了程序,返回退出码1,表示不正常退出。请注意,这样后面的代码将不会执行

方式二:try…catch

我们用try…catch:

在parse处Alt+Enter,选择Surround with try/catch,代码变为:

public class MyException {
    public static void main(String[] args) {
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
        try {
            Date date = sdf.parse("2021-0829");
        } catch (ParseException e) {
            e.printStackTrace();
        }
        System.out.println("执行后续代码...");
}

这样的话,显示了异常,后面的代码也还会执行,返回退出码0,表示正常退出。

既然有了这两种方式处理异常,我们再来看看几种变体:

变体一:自定义方法,均用throws

如果可能会异常的代码在自定义方法里,(两个throws都不能少)例如:

public class MyException {
    public static void main(String[] args) throws ParseException{
        method();
        System.out.println("执行后续main代码...");
    }
    public static void method() throws ParseException {
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
        Date date = sdf.parse("2021-0829");
        System.out.println("执行后续method代码...");
    }
}

运行结果如下,分析一下,这个异常是在进入method()执行到Date这一行的时候,抛出的异常。

method自己处理不了而终止,然后throws交给方法的调用者,也就是传给main函数处理。

但是main函数也是throws,同样处理不了而终止,最后交给虚拟机处理。

虚拟机处理的方式就是显示异常并终止程序,然后Date这一行下面的任务全部被终止,退出码1,非正常退出。

结果不管是method方法里面的sout,还是回到main()的sout,均未执行。

变体二:自定义方法,均用try…catch

其实不用都用try…catch,如果都用,那么会有重复现象,只能删掉main函数里面的try…catch。

那么实际上就只有一个try…catch了:

public class MyException {
    public static void main(String[] args) {
        method();
        System.out.println("执行后续main代码...");
    }

    public static void method() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        try {
            Date date = sdf.parse("2021-0829");
        } catch (ParseException e) {
            e.printStackTrace();
        }
        System.out.println("执行后续method代码...");
    }
}

这种情况是什么结果呢,在method方法中,自己处理异常后,执行catch{}里面的代码,打印出异常信息。

并继续执行了method的后续代码,然后返回main,又执行了main的后续代码,退出码0,正常退出。

所以这种方式, 两个后续代码均能执行,从代码上很容易知道是哪的问题,但是从输出上,很难看出来。

变体三:自定义方法,main中throws,method中try…catch

此时代码如下:

public class MyException {
    public static void main(String[] args) throws ParseException{
        method();
        System.out.println("执行后续main代码...");
    }

    public static void method() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        try {
            Date date = sdf.parse("2021-0829");
        } catch (ParseException e) {
            e.printStackTrace();
        }
        System.out.println("执行后续method代码...");
    }
}

这种情况的结果,步骤基本上同变体二,在method方法中,自己处理异常后,打印了异常输出,执行了method后续代码,然后返回main。

注意,method方法并没有把异常转给main处理,而是自己处理,(相当于儿子做错了事情,并没有告诉父亲)。

所以main中的throws并没有捕获到异常,然后执行main后续代码,最终退出码0,正常退出,但是两个后续代码均能执行,同样不容易发现是哪部分的问题。

再次思考,上面这个异常信息是method里面的try…catch还是main里面的throws?

答案肯定是method里面的try…catch。

不信的话我们把method方法里的e.printStackTrace();替换为System.out.println(e); 再来看看结果:

变体四:自定义方法,main中try…catch,method中throws

这个变体就有意思了,代码如下:

public class MyException {
    public static void main(String[] args){
        try {
            method();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        System.out.println("执行后续main代码...");
    }

    public static void method() throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date date = sdf.parse("2021-0829");
        //后续代码
        System.out.println("执行后续method代码...");
    }
}

这种情况,method里面出现了异常被throws捕获,自己处理不了导致method自身被终止。

传给给main处理,然后主函数的try…catch再来处理异常,打印异常信息,随后继续执行了main的后续代码,退出码0,程序正常退出。

(相当于儿子闯了祸进了局子,自己做不了事情了,然后父亲得知,替儿子收拾烂摊子,然后能做其他事)

好就好在,哪部分有问题,哪部分的后续代码就不继续执行了,这样问题就很明显了,所以推荐这种。

这里注意,这个异常是主函数打印出来的,不信我们把主函数的e.printStackTrace();替换为System.out.println(e); 然后再来看看结果:

总结:

上述讨论都是以ParseException为例,它是编译异常,是编译时程序出现的问题,必须用throws或者try…catch解决。

1. 如果用throws,那么会把异常交给方法的调用者处理(自己不处理,专门转给别人处理),那你转我也转,最终最终就交给java虚拟机处理,然后就是终止程序,后续代码不执行。

2. 如果用try…catch就是自己处理,不交给java虚拟机,后续代码也会执行。

所以工作中,我们一般采用变体四,即儿子闯祸向上抛(throws),交给最外层的父亲用try…catch处理,这样既能知道问题所在(儿子的后续动作都没执行,很容易看出来问题),又能执行main的后续代码,又能正常退出,何乐而不为呢!

举报
评论 0