【Java基础】Java异常的捕获及处理
《第一行代码:Java》第6章、异常的捕获及处理 读书笔记
文章目录
第6章、异常的捕获及处理
6.1 认识异常
异常是程序中导致程序中断的一种指令流,例:
package com.yootk.demo;
public class TestDemo {
public static void main(String args[]) {
System.out.println("1. 除法计算开始。") ;
System.out.println("2. 除法计算:" + (10 / 0)) ; // → 此处产生异常
// 出现异常并且没有正确处理后,异常之后的语句将不再执行
System.out.println("3. 除法计算结束。") ;
}
}
本程序将产生“ArithmeticException”异常,此时如果没有正确的处理异常,程序就会出现中断执行的情况,为了让程序在出现异常后依然可以正常执行完毕,需要引入异常处理语句来完善代码编写。
6.2 异常处理
*Java针对异常的处理提供了3个核心的关键字:try、catch、finally,利用这3个关键字就可以组成以下异常处理格式:
try {
// 有可能出现异常的语句
}[catch(异常类型 对象) {
// 异常处理
}catch(异常类型 对象) {
}catch(异常处理 对象) {
// 异常处理
}...][finally{
// 不管是否出现异常,都执行统一的代码
}]
- 如果在try中代码产生了异常,则程序会自动跳转到catch 语句中找到匹配的异常类型进行相应的处理。最后不管程序是否会产生异常,都会执行到finally 语句,finally 语句就作为异常的统一出口。异常基本处理流程如图6-1所示:
-
注意:finally和catchk块是可以省略的,但只能省略其中之一,异常处理格式一般有3种:try…catch…finally 、 try…catch 、 try…finally
-
例:
package com.yootk.demo; public class TestDemo { public static void main(String args[]) { System.out.println("1. 除法计算开始。"); try { System.out.println("2. 除法计算:" + (10 / 0)); // 此处产生异常 // 异常产生之后的语句将不再执行,此处在try中产生异常,所以下面的输出不会执行 System.out.println("更多课程请访问:www.yootk.com"); } catch (ArithmeticException e) { // 处理算术异常 System.out.println("******** 出现异常了 *********"); } System.out.println("3. 除法计算结束。"); } } /* 程序执行结果: 1. 除法计算开始。 ******** 出现异常了 ********* 3. 除法计算结束。 */
printStackTrace
上面代码虽然处理了异常,但它输出的异常描述不够准确,使用异常类中提供的 printStackTrace() 方法进行异常信息的输出。
package com.yootk.demo;
public class TestDemo {
public static void main(String args[]) {
System.out.println("1. 除法计算开始。");
try {
System.out.println("2. 除法计算:" + (10 / 0)); // 此处产生异常
// 异常产生之后的语句将不再执行,此处在try中产生异常,所以下面的输出不会执行
System.out.println("更多课程请访问:www.yootk.com");
} catch (ArithmeticException e) { // 处理算术异常
e.printStackTrace(); // 输出异常的完整信息
}
System.out.println("3. 除法计算结束。");
}
}
/*
程序执行结果:
1. 除法计算开始。
java.lang.ArithmeticException: / by zero
at com.yootk.demo.TestDemo.main(TestDemo.java:7)
3. 除法计算结束。
*/
6.3 异常的处理流程
为了能够正确地处理异常,就必须清楚异常的继承结构以及处理流程。
- 为了解释异常的继承结构,首先来观察以下两个异常类的继承关系:
通过这两个异常类可以发现所有的异常类的最高的继承类都是Throwable,并且在Throwable下有两个子类:
* Error:指的是JVM错误,这时的程序并没有执行,无法处理
* Exception:指的是程序运行中产生的异常,用户可以使用异常处理格式处理
- 清楚了异常类的继承关系后,下面了解一下Java中的异常的处理完整流程:
整个过程就好比方法传递参数一样,只是根据catch后面的参数类型进行匹配。既然异常捕获只是一个异常类对象的传递过程,那么依据Java 中对象自动向上转型的概念来讲,所有异常类对象都可以向父类对象转型,也就证明所有的异常类对象都可以使用 Exception来接收,这样就可以简单地实现异常处理了。
-
使用Exception来接收异常,因为所有异常类都是Exception的子类,所以可以使用 Exception来接收,这样就可以简单地实现异常处理了。
package com.yootk.demo; public class TestDemo { public static void main(String args[]) { System.out.println("1. 除法计算开始。"); try { int x = Integer.parseInt(args[0]); // 接收参数并且转型 int y = Integer.parseInt(args[1]); // 接收参数并且转型 System.out.println("2. 除法计算:" + (x / y)); // 此处产生异常 // 异常产生之后的语句将不再执行,此处在try中产生异常,所以下面的输出不会执行 System.out.println("更多课程请访问:www.yootk.com"); } catch (Exception e) { // 使用Exception接收异常,使其能处理所有异常类型 e.printStackTrace(); } finally { System.out.println("### 不管是否出现异常我都执行!") ; } System.out.println("3. 除法计算结束。"); } }
-
为什么不用Throwable来接收异常?
本程序使用Throwable来处理异常是没有语法错误的,因为Throwable是Exception的父类,自然能接收所有异常。但同样也会接收Error,但用户是没法处理Error错误的,所以开发中用户处理的异常都是以Exception类为主。
-
处理多个异常时,捕获范围小的异常要放在捕获范围大的异常之前处理
如:ArithmeticException要放在Exception前面处理,否则将出现语法错误。
6.4 throws关键字
throws关键字主要在方法定义时使用,表示此方法中不进行异常处理,而是交给被调用处处理。且调用这个方法的地方一定要做异常处理操作。
-
举例:
class MyMath { public static int div(int x, int y) throws Exception { // 表示此方法不处理异常 return x / y; } }
本方法中所产生的任何异常本方法都可以不用处理,而是直接交给程序的调用处进行处理。由于div()方法上存在throws抛出的Exception异常,则当调用此方法时必须明确地处理可能会出现的异常。
-
调用以上方法:
public class TestDemo { public static void main(String args[]) { try { // div()方法抛出异常,此处必须明确进行异常处理 System.out.println(MyMath.div(10, 2)); } catch (Exception e) { e.printStackTrace(); } } }
-
主方法上也可以使用throws抛出
主方法本身也属于一个Java方法,所以主方法上使用throws抛出,就表示在主方法里面可以不用强制性进行异常处理,如果出现异常,将交给JVM进行默认处理,则此时会导致程序中断执行。
public class TestDemo { public static void main(String args[]) throws Exception { // 表示此异常产生后会直接通过主方法抛出,代码中可以不强制使用异常处理 System.out.println(MyMath.div(10, 0)); } }
6.5 throw关键字
之前的所有异常类对象都是由JVM自动进行实例化操作得,而现在用户也可以自己手动地抛出一个实例化对象(手工调用异常类的构造方法),就需要通过throw完成。
-
手工抛出异常:
package com.yootk.demo; public class TestDemo { public static void main(String args[]) { try { // 直接抛出一个自定义的异常类对象 throw new Exception("自己定义的异常!"); } catch (Exception e) { e.printStackTrace(); } } } /* 程序执行结果: java.lang.Exception: 自己定义的异常 at com.yootk.demo.TestDemo.main(TestDemo.java:5) */
本程序首先实例化了一个Exception异常类对象,然后利用throw进抛出,这里就必须明确进行异常处理了。
-
throws和throw的区别:
- throws:在方法的声明上使用,表示此方法在定义时不抛出异常,而是在调用处抛出,且在调用处必须进行异常处理。
- throw:指的是在方法中人为抛出一个异常类对象(这个异常类对象可能是自己实例化的或者是抛出已存在的)
6.6 异常处理的标准格式
结合“try、catch、finally、throw、throws”一起使用的异常处理格式。
-
现要求定义一个div()方法,这个方法的要求为:
- 在进行除法操作之前,输出一行提示信息
- 在除法操作执行完毕后,输出一行提示信息
- 如果中间产生异常,则应该交给调用处进行处理
package com.yootk.demo; class MyMath { public static int div(int x, int y) throws Exception { // 出现异常要交给被调用处输出 System.out.println("===== 计算开始 ====="); // 等价于:资源打开 int result = 0; try { result = x / y; // 除法计算 } catch (Exception e) { throw e; // 向上抛 } finally { System.out.println("===== 计算结束 ====="); // 等价于:资源关闭 } return result; } } public class TestDemo { public static void main(String args[]) { try { System.out.println(MyMath.div(10, 0)); // 被调用处处理异常 } catch (Exception e) { e.printStackTrace(); } } } /* 程序执行结果: ===== 计算开始 ===== ===== 计算结束 ===== java.lang.ArithmeticException: / by zero at com.yootk.demo.MyMath.div(TestDemo.java:7) at com.yootk.demo.TestDemo.main(TestDemo.java:19) */
6.7 RuntimeException类
在Exception类中有一个RuntimeException子类,而对于所有的RuntimeException子类的对象,用户可以进行有选择性的处理,所以调用处即使不处理,也不会有任何编译语法错误。但如果没进行异常处理又发生了异常,将交由JVM进行默认处理。
-
比如Integer.parseInt()方法就会抛出一个NumberFormatException异常类对象,而这个异常类就属于RuntimeException子类。
package com.yootk.demo; public class TestDemo { public static void main(String args[]) { int temp = Integer.parseInt("100"); // 直接将字符串变为int型 System.out.println(temp); } } // 程序执行结果: 100
本程序没有处理Integer.parseInt()抛出的异常也能正常的编译和运行,但此时一旦出现异常,将交由JVM进行默认处理
6.8 assert关键字
assert关键字是JDK1.4引入的,其主要功能就是进行断言。
-
格式:
assert 判断语句:"异常说明";
如果判断语句的结果为false,则发生异常,并在错误信息处打印”异常说明“。
-
注意:Java默认是不开启断言的,需要用户手动开启
6.9 自定义异常
除了Java中已有的异常类型,还可以自己定义异常类型,只需要继承Exception类(强制性异常处理类)或RuntimeException类(选择性异常处理类)即可。
-
例,定义AddException:
package com.yootk.demo; class AddException extends Exception { // 此异常类要强制处理 public AddException(String msg) { super(msg); // 调用父类构造 } } public class TestDemo { public static void main(String args[]) { int num = 20; try { if (num > 10) { // 出现了错误,应该产生异常 throw new AddException("数值传递的过大!"); } } catch (Exception e) { e.printStackTrace(); } } } /* 程序执行结果: com.yootk.demo.AddException: 数值传递的过大! at com.yootk.demo.TestDemo.main(TestDemo.java:13) */
本章小结
- 异常是导致程序中断运行的一种指令流,当异常发生时,如果没有进行良好的处理,则程序将会中断执行。
- 异常处理可以使用try…catch 进行处理,也可以使用try…catch…finally进行处理,在 try语句中捕捉异常,之后在catch 中处理异常,finally作为异常的统一出口,不管是否发生异常都要执行此段代码。
- 异常的最大父类是Throwable,其分为两个子类:Exception、Error。Exception 程序处理的异常;而 Error表示JVM错误,一般不是由程序开发人员处理的。
- 发生异常之后,JVM 会自动产生一个异常类的实例化对象,并匹配相应的 catch语句中的异常类型,也可以利用对象的向上转型关系,直接捕获Exception。
- throws用在方法声明处,表示本方法不处理异常。
- throw表示在方法中手工抛出一个异常。
- 自定义异常类时,只需要继承Exception类或RuntimeException类。
y…catch…finally进行处理,在 try语句中捕捉异常,之后在catch 中处理异常,finally作为异常的统一出口,不管是否发生异常都要执行此段代码。 - 异常的最大父类是Throwable,其分为两个子类:Exception、Error。Exception 程序处理的异常;而 Error表示JVM错误,一般不是由程序开发人员处理的。
- 发生异常之后,JVM 会自动产生一个异常类的实例化对象,并匹配相应的 catch语句中的异常类型,也可以利用对象的向上转型关系,直接捕获Exception。
- throws用在方法声明处,表示本方法不处理异常。
- throw表示在方法中手工抛出一个异常。
- 自定义异常类时,只需要继承Exception类或RuntimeException类。
- 断言是JDK 1.4之后提供的新功能,可以用来检测程序的执行结果,但开发中并不提倡使用断言进行检测。
第7章、Eclipse开发工具
略