Java基础(三十一)
异常的捕获与处理
Java语言提供的最为强大的支持就在于异常的处理操作上
1:什么是异常
异常是导致程序中断执行的一种指令流。
下面观察没有异常产生的程序执行结果。
public class Test{
public static void main(String args[]) {
System.out.println("【1】程序开始执行");
System.out.println("【2】数学计算"+(10/2));
System.out.println("【3】程序执行完毕");
}
}
【1】程序开始执行
【2】数学计算5
【3】程序执行完毕
上述程序会按照既定的结构从头到尾开始执行。
下面观察产生异常的过程,
public class Test{
public static void main(String args[]) {
System.out.println("【1】程序开始执行");
System.out.println("【2】数学计算"+(10/0));
System.out.println("【3】程序执行完毕");
}
}
//【1】程序开始执行
//Exception in thread "main" java.lang.ArithmeticException: / by zero
// at 开始面试.Test.main(Test.java:8)
上述程序出现错误之后,整个程序将不会按照特定的方式进行执行,而是中断了执行;那么为了保证程序出现了非致命错误之后程序依然可以正常完成,所以就需要有一个完善的异常处理机制,以保证程序的顺利执行。
2处理异常
public class Test{
public static void main(String args[]) {
System.out.println("【1】程序开始执行");
try {
System.out.println("【2】数学计算"+(10/0));
}catch(ArithmeticException e ) {
System.out.println("处理异常"+e);//异常处理
}
System.out.println("【3】程序执行完毕");
}
}
//【1】程序开始执行
//处理异常java.lang.ArithmeticException: / by zero
//【3】程序执行完毕
上述代码中,此时发现现在即便出现了异常,程序也可以正常的执行完毕,所以此时的设计属于一个合理设计。
3:常类中提供的printStackTrace()方法。
但是有一个问题出现了:此时在进行异常处理的时候直接输出的是一个异常类的对象,那么对于此对象如果直接打印(上述代码中调用toString())所得到的异常信息并不完整,那么如果要想获得非常完整的异常信息,则可以使用异常类中提供的printStackTrace()方法。(观察如下代码)
public class Test{
public static void main(String args[]) {
System.out.println("【1】程序开始执行");
try {
System.out.println("【2】数学计算"+(10/0));
}catch(ArithmeticException e ) {
e.printStackTrace();
}
System.out.println("【3】程序执行完毕");
}
}
//【1】程序开始执行
//java.lang.ArithmeticException: / by zero
//【3】程序执行完毕
// at 开始面试.Test.main(Test.java:9)
4:finally程序块
对于异常的处理格式也可以在最后追加一个finally程序快,表示异常处理后的出口,不管是否出现异常执行。
public class Test{
public static void main(String args[]) {
System.out.println("【1】程序开始执行");
try {
System.out.println("【2】数学计算"+(10/0));
}catch(ArithmeticException e ) {
e.printStackTrace();
}finally {
System.out.println("【F】不管是否出现异常,我都会执行");
}
System.out.println("【3】程序执行完毕");
}
}
//【1】程序开始执行
//【F】不管是否出现异常,我都会执行
//【3】程序执行完毕java.lang.ArithmeticException: / by zero
//
// at 开始面试.Test.main(Test.java:9)
5:处理多个异常
很多时候在程序执行的过程中可能会产生若干个异常。那么这种情况下也可以使用多个catch进行异常的捕获。
public class Test{
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));
}catch(ArithmeticException e ) {
e.printStackTrace();
}finally {
System.out.println("【F】不管是否出现异常,我都会执行");
}
System.out.println("【3】程序执行完毕");
}
}
上述的代码可能产生三类异常。
1未处理:程序执行的时候没有输入初始化参数:
2未处理:输入的数据不是数字:
3已处理:输入的被除数为0,那么程序也会导致中断(finally代码依旧执行),所以在这样的情况下就必须进行多个异常的捕获。
修改代码如下:
public class Test{
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));
}catch(ArithmeticException e ) {
e.printStackTrace();
}catch(NumberFormatException e) {
e.printStackTrace();
}catch(ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
}
finally {
System.out.println("【F】不管是否出现异常,我都会执行");
}
System.out.println("【3】程序执行完毕");
}
}
上面的代码我们明确的知道了有哪些异常了,那么何必非要用异常处理呢,直接多写点判断不久可以了。(看后续)
6:异常处理流程
在进行异常处理的时候,如果将所有可能已经知道要产生的异常都进行了捕获,虽然可以得到非常良好的代码结构,但是这种代码编写非常麻烦,所以现在想要合理处理异常就必须清楚在异常产生后做了什么处理。
通过分析发现在整个的异常处理流程之中实际上操作的还是一个异常类的实例化对象,这个异常类的实例化对象的类型成为了理解异常处理的核心所在。
上面程序看到的两个异常:
可以发现在程序之中可以处理的异常的最大的类型就是Throwable;
Throwable提供有两个子类;
Error:此时程序还未执行出现的错误,开发者无法处理;
Exception:程序中出现的异常,开发者可以处理,真正在开发中需要关注的就是Exception;
通过分析发现异常产生的时候会产生的时候会产生异常的实例化对象,那么按照对象的引用原则,可以自动向父类转型,那么如果按照这样的逻辑,实际上所有的异常都可以使用Exception来处理。
public class Test{
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));
}catch(Exception e ) {
e.printStackTrace();
}
finally {
System.out.println("【F】不管是否出现异常,我都会执行");
}
System.out.println("【3】程序执行完毕");
}
}
上述代码中,当你不确定可能产生哪些异常的时候,这种处理方式是最方便的;
但是如果这样处理也会产生一个问题,这种异常的处理形式虽然方便,但是它描述的错误信息不明确,所以分开处理异常是一种可能更加明确的处理方式。
7:throws关键字
观察throws的使用
class MyMath{
//这个代码执行的时候可能会产生异常,如果产生异常调用处理
public static int div(int x,int y) throws Exception{
return x/y;
}
}
public class Test{
public static void main(String args[]) {
try {
System.out.print(MyMath.div(10, 0));
}catch(Exception e) {
e.printStackTrace();
}
}
}
//java.lang.ArithmeticException: / by zero
//at 开始面试.MyMath.div(Test.java:8)
//at 开始面试.Test.main(Test.java:14)
上述代码自己没有发现throws的用处
主方法本身也是一个方法,实际上主方法也可以继续向上抛出
class MyMath{
//这个代码执行的时候可能会产生异常,如果产生异常调用处理
public static int div(int x,int y) throws Exception{
return x/y;
}
}
public class Test{
public static void main(String args[])throws Exception {
try {
System.out.print(MyMath.div(10, 0));
}catch(Exception e) {
e.printStackTrace();
}
}
}
//java.lang.ArithmeticException: / by zero
//at 开始面试.MyMath.div(Test.java:8)
//at 开始面试.Test.main(Test.java:14)
如果主方法继续向上抛出异常,那么就表示此异常将交由JVM负责处理。
8:throw关键字
表示手工进行异常的抛出,并且进行异常的抛出处理。(观察下面代码的处理)
public class Test{
public static void main(String args[]) {
try {//异常对象不再是系统生成的,而是手工定义的
throw new Exception("自己定义的异常");
}catch(Exception e) {
e.printStackTrace();
}
}
}
//java.lang.Exception: 自己定义的异常
//at 开始面试.Test.main(Test.java:9)
9:throw和throws的区别
throw:是代码块中使用的,主要是手工进行异常对象的抛出;
throws:是在方法定义上使用的,表示将此方法中可能产生的异常明确告诉给调用处,由调用出进行处理。
10,异常处理的标准格式
class MyMath{
//异常交给被调用处处理则一定要在方法上使用throws
public static int div(int x,int y) throws Exception{
int temp = 0;
System.out.print("【开始】");
try {
temp = x/y;
}catch(Exception e) {
throw e;//向上抛异常对象
}finally {
System.out.print("【结束】");
}
return temp;
}
}
public class Test{
public static void main(String args[]) {
try {
System.out.print(MyMath.div(10,0));
}catch(Exception e) {
e.printStackTrace();
}
}
}
//【开始】【结束】java.lang.ArithmeticException: / by zero
//at 开始面试.MyMath.div(Test.java:10)
//at 开始面试.Test.main(Test.java:23)
对于此类操作实际可以简化,省略catch和throw操作
class MyMath{
//异常交给被调用处处理则一定要在方法上使用throws
public static int div(int x,int y) throws Exception{
int temp = 0;
System.out.print("【开始】");
try {
temp = x/y;
}finally {
System.out.print("【结束】");
}
return temp;
}
}
public class Test{
public static void main(String args[]) {
try {
System.out.print(MyMath.div(10,0));
}catch(Exception e) {
e.printStackTrace();
}
}
}
//【开始】【结束】java.lang.ArithmeticException: / by zero
//at 开始面试.MyMath.div(Test.java:10)
//at 开始面试.Test.main(Test.java:21)
11:RuntimeException
只要方法后面带有throws都是告诉用户本方法可能产生的异常是什么,观察下面代码。
public class Test{
public static void main(String args[]) {
int num = Integer.parseInt("123456");
System.out.print(num);
}
}
//123456
public static int parseInt(String s) throws NumberFormatException
这个方法上明确的抛出了一个异常,但是在处理的时候并没有强制性要求;
12:解释RuntimeException与Exception的区别
13:自定义异常类
14:assert关键字
所以在Java里面没有将断言设置为一个程序必须执行的步骤,需要特定的环境才可以开启。