JAVA中异常处理的一些问题
try-catch-finally使用的小细节
若出现异常语句,try内异常语句以下的语句皆不执行;
finally除了关闭程序必执行
class Demo01{
public static void main(String[] args) throws IOException {
haha();
}
public static void haha(){
try{
System.out.println("1");
System.out.println(10/0);
System.out.println("2");
System.out.println("3");
}catch(ArithmeticException e){
System.out.println("出现异常");
}finally {
System.out.println("12345");
}
}
}
结果:
1
出现异常
12345
Process finished with exit code 0(经过try-catch后程序正常走完,并没有被JVM继续返回异常提示)
通过上面的例子我们可以知道try语句在执行到System.out.println(10/0)时出现异常,后面的输出“2”和“3”的语句没有被执行。所以异常处理的过程是发现异常后,直接找try{//语句块}后面的catch{//语句块},如果catch所列出来的异常没有符合的,仍然会执行完finally,后继续抛给调用haha方法的main(),在抛给JVM。示例如下:
class Demo01{
public static void main(String[] args) {
haha();
}
public static void haha(){
try{
System.out.println("1");
System.out.println(10/0);
System.out.println("2");
System.out.println("3");
}catch(NumberFormatException e){
System.out.println("出现异常");
}finally {
System.out.println("12345");
}
}
}
结果:
1
12345
Exception in thread “main” java.lang.ArithmeticException: / by zero
at com.aaa.java.Demo01.haha(Demo01.java:63)
at com.aaa.java.Demo01.main(Demo01.java:57)
Process finished with exit code 1
得出结论:
① catch的异常通常情况写比较大的父类,而非具体的子类(除非对相应异常有特殊的处理),即利用多态性。如上图可以直接catch(RuntimeExceotion e){//语句块}
,会避免出现漏掉没发现的运行时异常(非受检异常),缺点就是不具有特殊处理。
②finally不管try语句和catch语句有没有执行完,都会被执行,除非程序被关闭(例如:被迫关机…或者在处理异常语句是:catch(IOExeption e){System.exit(0)}
)
try-catch-finally的return问题
来到了finally-return 问题。
第一个问题:try中有return,后面还有finally,谁先执行
class Demo01{
public static void main(String[] args){
haha();
}
public static void haha(){
try{
System.out.println("1");
return;
}catch(RuntimeException e){
System.out.println("出现异常");
}finally {
System.out.println("finally已执行");
}
}
}
结果:
1
finally已执行
Process finished with exit code 0
首先try语句块内是没有发生异常的,所以理论上会正常继续执行return语句,结束整个函数,但是我们看见结果并没有继续执行return,而是执行了finally语句块。经过debug发现语句的执行顺序是”打印1“->”执行finally中的语句“->”执行try中的return“。找了一些相关的博客,针对上面这一中情况的一句话是finally在try中的return执行之后,return调用返回之前执行!就是说return其实也是一个过程,在这个过程中,执行了finally语句,之后才完成return最后的返回。
所以不管是在try还是catch中,如果有return,都会执行finally语句先,才return完成函数,除了finally block里面自己已经有return了,那就没什么好说,直接return结束函数。
第二个问题:finally block里使用了try中的变量
return 基本数据类型
先来做几个小试验,仍然使用的是上面的haha函数,内容改了,main不变
public static void haha(){
try{
int a = 10;
return;
}catch(RuntimeException e){
System.out.println("出现异常");
}finally {
a = 20; // 在这里会报错,说i没有定义
}
System.out.println("haha函数还在执行");
}
上面这种情况报错的原因是 语句块的知识点,就是被{//语句块block}包含起来的看作一个单独存在的块,里面的定义的变量仅在块内的作用域使用,不被外面使用。下面定义整个函数的局部变量。
public static void haha(){
int a;
try{
a = 10;
return a;
}catch(RuntimeException e){
System.out.println("出现异常");
}finally {
a = 20;
// System.out.println(a);
}
System.out.println("haha函数还在执行");
}
结果:
10
这里的a是基本数据类型,return 返回值a没有被finally block改变;流程大概是这样(还没有找到确切证明的资料,通过查找各手资料和老师讲课):
调用方法,局部变量入栈,在try中的return执行的过程中,上面说了,return执行不仅仅是一个简单的返回语句,所以这里return会先调用并备份好数据值,注意是等号右边的value值,即10,放到不同于目前a所在的栈中的位置保留着;接着执行finally语句,fianlly语句中改变了a的值,改变的是放在栈中的局部变量a,这不影响已经被return保留好的a值,所以栈中a的值有变化,但是我们看不出来I v I,于是我在finally加了一条打印语句嘎嘎嘎,果不出吾所料,
结果是:
20
10
所以印证了这个观点,就是return是一个过程,会先保留要返回的值,finally改变的是栈中的值,而不是已经被return保留的值,所以在调用haha函数后,接收到return返回的值,即打印出来的值是 a = 10。
return 引用数据类型
然后我们再来一个小试验,关于引用类型的返回:
public class Demo1{
public static void main(String[] args) throws IOException{
System.out.println(haha().age);
}
public static Person haha() throws IOException{
Person p = new Person();
try{
p.age = 18;
return p;
}finally{
p.age = 28;
}
}
}
class Person{
int age;
}
结果是:28
这说明finally里面的修改被return收到了!和上面的结论貌似有点矛盾?
其实,是一样的道理,只是对于引用数据类型,传递的值是地址值!
所以return收到的是地址值,而finally修改的也是栈储存的实例对象地址值,进而修改实例的age值,所以对应上return 地址值对象的age,打印出来的便是被修改后的age=28。
第三个问题:对于基本数据类型,只有finally有return或者try和finally都有return
这个时候return的都是finally语句中return的。
但实际上这样写会被警告⚠,说finally没有被完整执行,查了一下,是因为finally会把异常吃掉。
public static int haha(){
int i;
try{
i = 10;
return i;
}finally{
i = 20;
return i; //这里出现警告
}
}
可以看这位博主的blog:https://www.cnblogs.com/interdrp/p/4095846.html
但是结果如我们所想,就是20。
throws和try-catch什么时候用
我上一篇blog写到,但是不是很清楚,这里重新写一下:
public class Demo01 {
public static void main(String[] args) throws IOException {
go("....."); // .....为要输入的text
}
public static void go(String text) throws IOException {
Runtime.getRuntime().exec(text);
}
}
比如说这里,事实上如果不做异常处理在IDEA或者Eclipse中都会有警告,我这里代码加了抛出异常,当然最后是抛给了JVM,如果真的出错且没被catch住,那最后只能是中断程序。
这里为什么使用throws呢?
因为这里会出现异常的原因,是传入的参数 text 可能有错误,就不是这个函数所能够接收的指令。这个时候,**并不是我们的go函数本身出错,**而是传入的参数有错,所以我们要在传参的时候进行异常处理,将异常抛给调用这个函数的地方,检查参数是否合理,再传入go函数。
好啦,本次在学习异常处理的一些小问题大概经过各种搜集大佬们的资料讲解加上自己的一些验证,如果发现有什么问题解释的不正确的欢迎大家指正鸭!
然后抛出 引用dalao们的blog:
CSU_Dennis finally在return前还是后进行 解析
b766224 深入理解finally关键字,Finally到底是在return前面执行还是在return后面执行
公众号:程序员与王子喵 欢迎关注 finally和return的执行顺序,从根本上理解