25、异常捕获与处理

合理使用异常处理,可以让程序更加健壮。

异常的产生

异常是导致程序中断执行的一种指令流。当异常出现时,如果没有合理处理,程序就会中断执行。
范例:不产生异常的代码

public class Demo {
    public static void main(String[] args) {
        System.out.println("1.除法计算开始");
        System.out.println("2.出发计算:10 / 2 = " + (10 / 2));
        System.out.println("3.除法计算结束");
    }
}

范例:产生异常

public class Demo {
    public static void main(String[] args) {
        System.out.println("1.除法计算开始");
        // 2.中将出现异常
        System.out.println("2.出发计算:10 / 2 = " + (10 / 0));
        System.out.println("3.除法计算结束");
    }
}

// 结果为:
// 1.除法计算开始
// Exception in thread "main" java.lang.ArithmeticException: / by zero
// at com.java.util.Demo.main(Demo.java:7)

异常产生后,产生异常的语句以及之后的语句将不再执行。默认情况下系统会输出异常信息,而后自动结束程序的执行。

异常的处理

1、Java中进行处理异常,使用trycatchfinally这三个关键字,语法如下:

try {
    // 可能出现异常的语句
} catch (异常类型 对象1) {
    // 异常处理
} catch (异常类型 对象2) {
    // 异常处理
} finally {
    // 不论是否出现异常都执行的语句
}

对于上述操作的组合有:try…catchtry…catch…finallytry…finally(这个不建议使用)。
范例:应用异常处理格式

public class Demo {
    public static void main(String[] args) {
        System.out.println("1.除法计算开始");
        try {
            System.out.println("2.出发计算:10 / 2 = " + (10 / 0));
        } catch (ArithmeticException e) {

        }
        System.out.println("3.除法计算结束");
    }
}

// 结果为:
// 1.除法计算开始
// 3.除法计算结束

2、出现异常就要处理异常,为了能进行异常处理,可以使用异常类中的printStackTrace()输出完整的异常信息:

public class Demo {
    public static void main(String[] args) {
        System.out.println("1.除法计算开始");
        try {
            System.out.println("2.出发计算:10 / 2 = " + (10 / 0));
        } catch (ArithmeticException e) {
            e.printStackTrace();
        }
        System.out.println("3.除法计算结束");
    }
}

// 结果为:
// 1.除法计算开始
// java.lang.ArithmeticException: / by zero
// at com.java.util.Demo.main(Demo.java:7)
// 3.除法计算结束

范例:使用try…catch…finally

public class Demo {
    public static void main(String[] args) {
        System.out.println("1.除法计算开始");
        try {
            System.out.println("2.出发计算:10 / 2 = " + (10 / 0));
        } catch (ArithmeticException e) {
            e.printStackTrace();
        } finally {
            System.out.println("不论是否异常,都执行");
        }
        System.out.println("3.除法计算结束");
    }
}

3、异常捕获时,一个try语句可以跟多个catch语句。

public class Demo {
    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("不论是否异常,都执行");
        }
        System.out.println("3.除法计算结束");
    }
}

上述程序将由用户输入数据,可能存在以下异常:
· 用户执行时不输入参数(java Demo):java.lang.ArrayIndexOutOfBoundsException数组越界错误;
· 输入的数据不是数字(java Demo a b):java.lang.NumberFormatException
· 被除数为0(java Demo 10 0):java.lang.ArithmeticException
以上代码只有一个catch,只能处理一个异常,其他异常依然会导致程序中断
范例:增加多个catch

public class Demo {
    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("不论是否异常,都执行");
        }
        System.out.println("3.除法计算结束");
    }
}

异常处理流程

1、观察两个异常类的继承结构:

NumberFormatException:	
java.lang.Object
  |- java.lang.Throwable
      |- Exception
          |- RuntimeException
              |- IllegalArgumentException
                  |- NumberFormatException	
ArithmeticException:
java.lang.Object
  |-java.lang.Throwable
      |- Exception
          |- RuntimeException
              |- ArithmeticException

由上表可得,所有异常类都是Throwable的子类。Throwable下有两个子类ErrorException

请解释Error和Exception的区别:
· Error:指的是JVM错误,即:此时程序还没有执行,用户不能处理;
· Exception:指的是程序运行中产生的异常,用户可以处理。

所谓的异常处理指的是Exception以及它的子类异常。
2、异常处理流程图
在这里插入图片描述

流程描述:
1)当程序运行时出现异常,由JVM自动根据异常类型实例化一个与之类型匹配的异常类对象;
2)产生异常对象后,会判断当前语句是否存在异常处理。如果没有异常处理,就交给JVM进行默认的异常处理(输出异常信息,结束程序调用);
3)如果有异常捕获操作,会由try语句捕获产生的异常类实例化对象,之后与catch语句进行比较,如果有符合的捕获类型,则使用catch语句进行异常处理;如果不匹配,则继续向下匹配其它catch语句;
4)不论异常处理是否能够匹配,都要继续执行,如果程序中存在finally语句,就先执行finally语句中的代码,执行完毕后根据之前catch匹配结果来决定如何执行,如果之前成功捕获异常,那就继续执行finally之后的语句;如果没有成功捕获,就交给JVM进行默认处理。
整个过程和catch中的异常类型进行匹配,但是所有Java对象都可以自动向上转型。即如果真的要匹配类型,简单的做法就是匹配Exception。

public class Demo {
    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("不论是否异常,都执行");
        }
        System.out.println("3.除法计算结束");
    }
}

上述将所有的异常都交由Exception类处理,因此程序无法知道具体产生的是什么异常。

说明:
· 使用多个catch时,范围大的异常一定要放在范围小的异常后面,否则会出现语法错误。
· 直接捕获Exception比较方便,但不合理,因为所有异常都按照同种方式处理。项目中应根据具体异常类型处理。

throws关键字

1、throws关键字主要用于方法声明,将异常交由被调用处(如main方法)处理。
范例:使用throws关键字

class MyMath {
    public static int div(int x, int y) throws Exception {
        // 使用了throws,所以该方法产生的异常交由调用处处理
        return x / y;
    }
}

范例:调用上述方法

public class Demo {
    public static void main(String[] args) {
        // 必须进行异常处理,否则代码报错
        try {
            System.out.println(MyMath.div(10, 2));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

调用了具有throws声明的方法,不论操作是否异常,都需要使用try..catch进行异常处理。
2.在主方法使用throws关键字后,异常将交给JVM处理,即采用默认处理方式。由于开发的程序多数希望正常结束调用,因此主方法不应该使用throws关键字。

public class Demo {
    public static void main(String[] args) throws Exception {
        System.out.println(MyMath.div(10, 0));
    }
}

throw关键字

1、程序中可以使用throw手工抛出一个异常类的对象。

public class Demo {
    public static void main(String[] args) {
        try {
            throw new Exception("自定义异常");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

throws与throw的区别:
·throw:在方法中手工抛出一个异常类对象(该对象可以是自定义的,或者已经存在的);
·throws:用于方法声明上,使得调用该方法时必须处理异常。

异常处理标准格式

要求:定义div(),在执行除法前打印提示信息,在计算结束后打印提示信息;如果计算中产生了异常,交给调用处处理。
范例:

class MyMath {
    public static void div(int x, int y) {
        System.out.println("==== 除法计算开始 ====");
        System.out.println(x / y);
        System.out.println("==== 除法计算结束 ====");
    }
}

public class Demo {
    public static void main(String[] args) {
        MyMath.div(10,2);
    }
}

上述代码可能出现异常,因此要进行异常处理。根据要求,异常交由调用处处理,因此使用throws关键字。

class MyMath {
    // 如果div()出现异常,异常交给调用处处理
    public static void div(int x, int y) throws Exception {
        System.out.println("==== 除法计算开始 ====");
        System.out.println(x / y);
        System.out.println("==== 除法计算结束 ====");
    }
}

public class Demo {
    public static void main(String[] args) {
        try {
            MyMath.div(10, 2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

上述代码产生错误后,程序运行到System.out.println("==== 除法计算结束 ====");就不执行了。
范例:正确做法如下:

class MyMath {
    // 如果div()出现异常,异常交给调用处处理
    public static void div(int x, int y) throws Exception {
        System.out.println("==== 除法计算开始 ====");
        try {
            System.out.println(x / y);
        } catch (Exception e) {
            throw e; // 抛出异常
        } finally {
            System.out.println("==== 除法计算结束 ====");
        }
    }
}

public class Demo {
    public static void main(String[] args) {
        try {
            MyMath.div(10, 0);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

RuntimeException类

范例:观察下述程序

public class Demo {
    public static void main(String[] args) {
        int temp = Integer.parseInt("100");
    }
}

parseInt(): public static int parseInt(String s) throws NumberFormatException;
parseInt()抛出了NumberFormatException,按照之前知识点,此处应强制进行异常捕获,但实际并没有该要求:
观察一下NumberFormatException的继承结构:

java.lang.Object
  |- java.lang.Throwable
      |- java.lang.Exception
          |- java.lang.RuntimeException → 运行时异常
              |- java.lang.IllegalArgumentException
                  |- java.lang.NumberFormatException

Java为方便代码编写,提供了RuntimeException类,该类的特征是:程序在编译时,不会强制性要求用户处理异常,用户可以根据自己的需求选择性处理,但是没有处理又发生异常,就会交给JVM默认处理。

请解释Exception与RuntimeException的区别,请列举常见的几种RuntimeException
· Exception是RuntimeExceptio的父类;
· 使用Exception定义的异常必须要被处理,而RuntimeException的异常可以选择性处理。
·常见的RuntimeException:ArithmeticException、NullPointerException、ClassCastException。

异常的捕获及处理(断言)

assert关键字在JDK1.4引入,其功能是进行断言。

public class Demo {
    public static void main(String[] args) {
        int num = 10;
        assert num == 20 : "num 不等于20";
        System.out.println("num = " + num);
    }
}

默认情况下,断言是不应该影响程序的运行,即Java在解释程序时,断言是默认不起作用的。
启用断言:java -ea Demo

Exception in thread "main" java.lang.AssertionError: num的内容不是20
        at Demo.main(Demo.java:6)

异常的捕获及处理(自定义异常)

  1. Java自身提供了大量的异常类,但对于实际开发是不够。例如:进行添加数据操作时,可能出现错误数据,错误数据出现就应该抛出异常,例如AddException,而该异常Java没有,需要自己开发。
    如果要自己开发一个异常类可以选择继承Exception或RuntimeException。
    范例:定义AddException
class AddException extends Exception {
    public AddException(String msg) {
        super(msg);
    }
}

public class Demo {
    public static void main(String[] args) {
        int num = 11;
        try {
            if (num > 10) {
                throw new AddException("数值传递过大");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

上述代码,只是介绍自定义异常的形式,不能说明自定义异常的作用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章