Java 复习笔记5 - 异常处理
异常处理
异常分类与体系结构:
error 相关类型的异常是程序无法处理(大多都是修改代码无法解决的)的异常,这类异常通常需要我们调整jvm的运行环境
exception 相关类型的异常是程序可以处理的异常,其包含两大子类型
-
编译异常(checkedexception)
通常是语法错误,或是方法明确指明可能抛出异常则必须捕获处理
-
运行时异常(runtimeexception)
指的是检查阶段没有发现任何问题,满足所有语法规范,只有在运行时才能发现的异常
异常处理
关键字 try catch finally throws throw
基本语法:
注意:
- try 无法单独使用,必须与catch 或finally 组合使用
finally 表示最终,即无论异常是否真的发生了,最终都会执行finally
- 当异常类型为exception时 表示通用异常处理(万能异常处理),可以捕获所有异常但是无法针对性的处理
- 一个try可以有多个catch
- 通常将exception添加到最后一个catch中作为完全保证,让程序可以继续运行
- 一个try中的所有catch下每个异常类型只能出现一次
- catch中异常类型必须从小到大(先子类在父类)
- 无论有多个catch最终只有一个会被执行(类似 if else.....)
如要打断finally的执行可以直接退出虚拟机,system.exit();
finally 对return的影响
在方法中,如果有finally,即使在 try 或catch中遇到了return 语句,方法也不会立即结束,而是必须执行完finally后才会执行结束,
案例:
class test{ public static void main(string[] args){ system.out.println(func(0)); //此处得到的结果为10000; //如果没有finally 则是10; } public static int func(int arg){ try{ int a = 1/arg; return a; }catch(exception e){ return 10; }finally{ return 10000; } } }
强调:方法中无论是否出现异常,返回值都以finally中的返回值为准,这是不符合实际的,所以通常不这么写
抛出异常
throws
用于方法定义,在定义方法时可以使用throws来声明,方法可能会抛出某种类型的异常
什么时候使用throws
当方法中可能会有异常,但是方法本身无法处理,或是不想处理....就可以使用throws来抛出
需要强调的是:如果一个异常抛出后没最终没有得到处理,将导致程序运行中断,所以通常我们不能放任不管
使用
注意:
简单的说,只要声明了,则表明方法内部不会处理该异常,谁调用就由谁处理;
- 相应的调用者则需要捕获异常并处理,若调用方也无法处理,则可以继续向上抛出
一个方法也可以声明抛出多种不同类型异常,响应的调用方应增加对应的catch
案例:
package com.yh.test; public class test { public static void main(string[] args){ //一直抛向程序入口都没有得到处理则程序被终止 func3(); } static public void func2() throws arithmeticexception{ int a = 1/0; } static public void func3() throws arithmeticexception{ try{ func2();//func2抛出异常 }catch (exception e){ //无法处理则继续向上抛出 throw e; } } }
抛出多种类型异常:
import java.util.inputmismatchexception; import java.util.scanner; public class test { public static void main(string[] args){ try { func3(); } catch (arrayindexoutofboundsexception e) { e.printstacktrace(); } catch (inputmismatchexception e) { e.printstacktrace(); } } static public int func3() throws arrayindexoutofboundsexception,inputmismatchexception{// try{ int a = new scanner(system.in).nextint(); return 10/a; } }
throw
主动抛出异常
使用场景:
当方法的提供者需要调用者提供一些数据,但是这些数据不合法,导致功能无法正常处理时,就应该主动抛出异常
调用方可以在捕获到异常后进行处理
抛出还是处理?
方法内部导致的异常自己处理,调用者导致的异常则抛出(或者能处理则处理)
疑惑:为什么throws exception时编译器要求必须提供解决方案?
因为exception 既包含了运行时异常 又包含了 编译时异常,所以要求必须处理,由此可知,当throws声明的异常为编译时异常时则要求必须提供处理代码;
案例:
package com.yh.test; public class test { public static void main(string[] args){ try { func(1); } catch (exception e) { e.printstacktrace(); } } static void func(int a) throws exception{ if (a < 1){ throw new exception("参数不能小于1"); } } }
自定义异常
当内置异常无法满足特点业务场景时,可以自定义异常类,只需要继承exception即可
案例:
class myexception extends exception{ public myexception() { super("固定的异常提示!"); } }
当异常提示信息固定的时候可以像上面一样定义构造器
异常链
当一个方法抛出的异常被调用方法捕获,但是调用方法又抛出了新的异常时,则形成了异常链,
如果我们在调用方法中直接抛出新的异常不做任何额外的操作时,原始异常信息则被丢失;
我们有两个方法可以保留原始异常信息
1.实例化时使用原始异常作为参数
2.实例化后调用initcause传入原始异常对象
案例:
package com.yh.test; public class exceptionlinketest { public static void main(string[] args) { try { func2(); } catch (exception e) { e.printstacktrace(); } } static public void fun1() throws arithmeticexception{ int a = 1 / 0; } static public void func2() throws exception{ try{ fun1(); }catch (arithmeticexception e){ //不保留原始异常信息 //throw new exception("新的异常信息!"); //保留异常信息方法1 throw new exception("新的异常信息!",e); //保留异常信息方法2 exception newex = new exception("新的异常信息!"); newex.initcause(e); throw newex; } } }
当我们不做处理时,得到的异常信息如下:
使用 方法1或方法2 来保留异常信息结果如下: