4.0Java异常的引入
1.异常——程序不正常的行为或状态,即程序运行到某处时未按照原设计意愿进行。但语法错误和逻辑错误不是异常,对应着Java运行错误处理机制
异常的例子:int a = 5/0;数组越界访问;读取文件,结果该文件不存在
2.异常处理——处理异常
使程序回到安全状态;
允许用户保存结果,并以适当方式关闭程序;
(在操作系统层面上的对异常的处理:win98一旦出现错误直接蓝屏,所有正在写的代码、文档等等都不会保存;win7/10出现错误时一般是弹窗警告且不会影响其它没有异常的程序)
4.1异常分类
Throwable类:所有异常最顶端的父类
(1)Error类: JVM相关的异常——内部错误或者资源耗尽,由系统保留(不管)
(2)Exception类: 程序有关的异常,供应用程序使用,一般所说的异常是指Exception类及其子类
①Runtime Exception及其子类: 程序自身的错误
Runtime Exception类的子类称Unchecked Exception类(不受检的异常),是编译器不会辅助检查的、需程序员自己管的异常。以预先判定以避免为主。
例:5/0 ,空指针,数组越界,数字格式不对
②非Runtime Exception类及其子类:外界相关的错误
非Runtime Exception类及其子类称Checked Exception类(受检的异常),是编译器会辅助检查的异常,检查到会提醒,未处理则通不过编译。以发生异常后处理以避免为主。
例:打开一个不存在文件;加载一个不存在的类;连接一个不存在的数据库
注:IO Exception(输入输出异常)是非Runtime Exception的典型示例
4.2.1异常处理(一)
1.Java处理异常基本流程:①抛出(throw)异常:只管抛出,不一定在本方法内处理,至于到底谁去处理 ②会在运行时系统在调用栈中查找——从生成异常的方法开始进行回溯,直到找到:③捕获(catch)异常的代码
2.相关语句格式:
(1)抛出异常
throw异常对象;
注:在程序中采用throw主动抛出异常时:在方法的内部,抛出异常采用throw关键字;在方法的声明中,声明抛出异常采用throws关键字
(2)捕获异常
try{
语句组
}catch(异常类名 异常形式参数名){
异常处理语句组;
}catch(异常类名 异常形式参数名){
异常处理语句组;
}finally{
异常处理语句组;
}
注:其中,catch语句可0至多个,可没有finally语句
3. Exception类——程序相关异常的最高父类
(1)构造方法
public Exception(); //默认无参构造方法
public Exception(String message);//有参构造方法1
Exception(String message, Throwable cause) ; //有参构造方法2,信息+底层原因
(2)普通方法
getMessage();//得到信息的getter方法
getCause();//得到底层原因的getter方法
printStackTrace();//该方法没有返回值,它的功能是完成一个打印操作,在当前的标准输出(一般就是屏幕显示)上打印输出当前例外对象的堆栈使用轨迹,也即程序先后调用并执行了哪些对象或类的哪些方法,使得运行过程中产生了这个异常对象。
4.多异常的处理:由于catch块的异常匹配是从上而下进行匹配,所以子类异常(小异常)要写在父类异常(大异常)的前面。小异常:一般是指具体的异常子类,如Arithmetic Exception;大异常:一般是泛指一种错误类型的异常父类,如Exception。
5.受检的异常(非Runtime Exception类及其子类称Checked Exception类),要求明确进行语法处理:
(1)要么捕(catch)
(2)要么抛(throws):在方法签名后用throws xxxx来声明,这个声明表示该方法有抛出异常的可能
①当调用声明过抛出异常的方法时,仍然是要么捕要么抛,直至main方法。而在main方法内,仍然是要么捕要么抛,构成了一个严格体系。
②如果父类的抛出异常的方法被覆盖,那么覆盖它的子类方法要么抛出相同的异常、要么抛出子类异常。如果父类的方法抛出多个异常,那么重写的子类方法必须抛出那些异常类或其各子类异常。总之子类重写的方法所声明的异常,不能超出被覆盖的父类方法中所声明的异常范围。
4.2.2异常处理(二)
1.异常处理的目的:
(1)允许用户及时保存结果
(2)抓住异常,分析异常内容
(3)控制程序返回到安全状态
2. try-catch-finally: 一种在程序运行过程中,遇到异常时使代码正常继续向下执行的机制。
(1)结构(try 必有,catch 和finally至少有一个,catch可有多个):
try…catch
try…catch…finally
try…finally
①try: 实现业务的代码
②catch: 当try发生异常时,将立即跳跃到catch 代码执行,并不再返回到try语句。若无异常,跳过catch语句。
③finally: 当try或catch执行结束后,必须要执行finally
注: 即使catch内部再次发生异常时,也不影响finally的最终执行
(2)关于catch
①catch块可以有多个,每个都有相互不同的入口形参。当已发生的异常和某一个catch块中的形参类型一致,那么将执行该catch 块中的代码 。如果没有匹配的,catch则不会被触发。最后都进入 finally 块。
②进入catch块后,既再不会返回到try中发生异常的位置,更不会执行后续的catch块。即一个异常只能进入一个catch块。
(3)关于finally
①try结构中,只要有finally块,finally语句就肯定被执行(即使前面有break语句、return语句,即便try、catch内都发生了异常)
②try catch finally每个模块里面也允许发生异常,所以也可以在每个模块的内部继续写完整的try结构(不允许嵌套不完整的try结构,如双层try、双层catch)。
(4)三个例子:
- public class TryDemo {
- public static void main(String[] args) {
- try
- {
- int a = 5/2; //无异常,正常执行
- System.out.println("a is " + a);//无异常,正常执行
- }
- catch(Exception ex)//try内无异常,则catch语句被跳过
- {
- ex.printStackTrace();
- }
- finally//try内无异常,catch被跳过,最终执行finally
- {
- System.out.println("Phrase 1 is over");
- }
- //当try发生异常时,将立即跳跃到catch 代码执行,并不再返回到try语句。
- try
- {
- int a = 5/0; //ArithmeticException,直接跳到catch语句
- System.out.println("a is " + a);//上一行出现异常,本行不被执行
- }
- catch(Exception ex)//从出现异常的行直接跳跃到该行执行
- {
- ex.printStackTrace();//无异常,正常执行
- }
- finally//try内有异常,直接跳跃到catch执行,最后执行finally
- {
- System.out.println("Phrase 2 is over");
- }
- //catch内部再次发生异常时,也不影响finally的最终执行
- try
- {
- int a = 5/0; //ArithmeticException,直接跳到catch语句
- System.out.println("a is " + a);//上一行出现异常,本行不被执行
- }
- catch(Exception ex)
- {
- ex.printStackTrace();//无异常,正常执行
- int a = 5/0; //ArithmeticException,但程序不会在此处停止
- //而是直接跳跃到finally语句去执行
- }
- finally//catch内部再次发生异常时,不影响finally的最终执行
- {
- System.out.println("Phrase 3 is over");
- }
- }
- }
输出:
4. throws:当程序可能存在异常,但我处理不了时,那么就可以用throws来声明异常。
(1)调用带有throws checked exception的方法,要么catch这些异常,要么再次throws异常直至main方法,否则无法通过编译。
(2)用try-catch处理异常
输出:
(3)用try-catch处理异常 / 再次向外throws异常
4.3自定义异常类
1.简要复习:
(1)Throwable:所有异常的最顶端的父类,Exception类是所有程序相关异常类的父类。
(2)Exception类也继承自java.lang.Throwable,Error类与其在同一层级。但Error类异常一般是系统层面的,无需程序处理;程序只需要处理Exception。
2.Java在设计时提供了很多异常类,但有时仍不能满足需求,故需自定义异常类
3.自定义异常类时需继承Exception类或其子类,可以定义属性、方法(也可以重写父类的方法):
(1)继承自非Runtime Exception类及其子类:新定义的异常类就是Checked Exception
(2)继承自Runtime Exception类及其子类: 新定义的异常类就是Unchecked Exception的子类
4.自定义异常重点在构造函数
(1)调用父类Exception的message构造函数
(2)可以自定义自己的成员变量
5.通过下例理解自定义异常如何工作:
(1)MyException.java
- public class MyException extends Exception {
- //定义两个成员变量
- private String returnCode ; //异常对应的返回码
- private String returnMsg; //异常对应的描述信息
- //默认无参构造函数
- public MyException() {
- super();//调用父类无参构造方法
- }
- //有参构造函数1
- public MyException(String returnMsg) {
- super(returnMsg);//调用父类的参数列表为returnMsg的构造方法
- this.returnMsg = returnMsg;
- }
- //有参构造函数2
- public MyException(String returnCode, String returnMsg) {
- super();//调用父类无参构造方法
- this.returnCode = returnCode;
- this.returnMsg = returnMsg;
- }
- //两个getter方法
- public String getReturnCode() {
- return returnCode;
- }
- public String getreturnMsg() {
- return returnMsg;
- }
- }
(2)MyExceptionTest.java
- public class MyExceptionTest {
- //在方法的内部,抛出异常采用throw关键字;在方法的声明中,声明抛出异常采用throws关键字
- public static void testException() throws MyException {
- throw new MyException("10001", "The reason of myException");
- }
- //调用有异常的方法,要么try-catch处理异常,要么让再次throws异常直至main方法
- public static void main(String[] args) {
- try {
- MyExceptionTest.testException();
- } catch (MyException e) {
- e.printStackTrace();
- System.out.println("returnCode:"+e.getReturnCode());
- System.out.println("returnMsg:"+e.getreturnMsg());
- }
- }
- }
6. 重抛异常及异常链接:对于异常,不仅要进行捕获处理,有时候还需将此异常进一步传递给调用者,以便让调用者也能感受到这种异常。这时可以在catch语句块或finally语句块中采取以下三种方式:
(1)将当前捕获的异常再次抛出:
throw e;
(2)重新生成一个异常,并抛出,如:
throw new Exception("some message");
(3)重新生成并抛出一个新异常,该异常中包含了当前异常的信息,如:
throw new Exception("some message",e) ; //可用e.getCause() 来得到内部异常
注:本文由H同志编写,欢迎批评指正、交流探讨。