4. 异常

4.1 异常定义

异常:就是程序在运行的过程中遇到的种种不正常的情况。

特点:如果一个程序在运行中遇到了一个未经处理的异常,则这个异常会终止程序的运行。

但是如果程序出现的异常被处理了,此时程序不会被终止。所以我们需要知道怎么去处理异常。

其实在Java中,异常也是一个类。

4.2 异常类的体系

Throwable: 是所有的异常的根类

  1. Throwable: 它是所有错误与异常的超类
  2. |- Error 错误
  3. |- Exception 编译期异常,进行编译JAVA程序时出现的问题
  4. |- RuntimeException 运行期异常, JAVA程序运行过程中出现的问题

Error: 发生在编译器级别的,我们程序无法处理的错误。 VirtualMachineError:OutOfMemoryError, StackOverflowError,

  1. public static void main(String[] args) {
  2. int[] arr = new int[1024*1024*100];
  3. //该句运行时发生了内存溢出错误OutOfMemoryError,开辟了过大的数组空间,导致JVM在分配数组空间时超出了JVM内存空间,直接发生错误。
  4. }

Exception: 我们可以去处理的异常。

  1. public static void main(String[] args) {
  2. int[] arr = new int[3];
  3. System.out.println(arr[0]);
  4. System.out.println(arr[3]);
  5. System.out.println("over");
  6. }

4.3 Exception的分类

可以分成两种异常:

  • 运行时异常(Runtime Exception)

发生在程序运行的过程中的异常。

如果不处理这种异常,程序可以正常编译,但是当执行到异常产生的时候,会终止程序的运行。

例如:NullPointerException、IndexOutOfBoundsException、ArithmeticException…

  • 非运行时异常(Non-Runtime Exception)(编译异常)

发生在程序编译的过程中的异常。

如果不处理这种异常,程序将无法进行编译。

例如:ParseException…

4.4 常见异常

java.lang.RuntimeException
ClassCastException
ArrayIndexOutOfBoundsException
NullPointerException
ArithmeticException
java.io.IOExeption
FileNotFoundException
EOFException
java.lang.ClassNotFoundException
java.lang.InterruptedException
java.io.FileNotFoundException
java.sql.SQLException

4.5 异常处理

4.5.1方式一:捕获异常

语法:

  1. try {
  2. // 这里写可能会产生异常的代码。
  3. // 注意:
  4. // 一旦这里面的代码产生了异常,从异常产生开始往后所有try中的代码都不再执行,直接执行指定的catch
  5. }
  6. catch(需要捕获的异常类型 标识符) {
  7. // 捕获异常,如果try中产生的异常类型和我们要捕获的异常类型匹配,此时会执行这个代码段中的内容
  8. // 如果执行到这里了,相当于这个异常被捕获、处理了,这个异常将不再终止程序的运行。
  9. }
  10. catch(需要捕获的异常类型 标识符) {
  11. ......
  12. }
  13. finally {
  14. // 这里的代码始终会执行。
  15. // 无论try中的代码有没有异常产生,这里的代码都会执行。
  16. // 在这里我们一般情况下是做一些资源释放的工作。
  17. }

注意:

  • 以上,是完整的try-catch-finally语句。但是实际在用的时候,try后面可以只有catch, 也可只有finally,但是不能什么都没有。
  • 一般情况下,catch我们是不会省略不写的。
  • 如果try中的代码可能产生的异常不止一种

    • 如果需要对产生的不同异常进行不同的处理,可以使用多个catch语句

      • 多个catch语句的先后顺序
      • 如果多个catch中的异常,没有继承关系,则先后顺序没有影响
      • 如果多个catch中的异常,有继承关系,则子类异常在前,父类异常在后
    • 如果需要对某些异常做同样的处理,可以在同一个catch中,用 | 拼接所有要处理的异常。这些用|拼接起来的异常之间,不能有继承关系
    • 如果需要对所有的异常做同样的处理,可以在一个catch中捕获一个父类异常。
  1. public static int show(int a, int b) {
  2. int c = 0;
  3. try {
  4. c = a / b;
  5. // 能走到这里,说明上面的除没有异常。
  6. return c;
  7. }
  8. catch (ArithmeticException e) {
  9. System.out.println("出现了一个算术异常");
  10. return c;
  11. }
  12. finally {
  13. // 在return之前,执行finally中的代码段
  14. System.out.println("finally中的代码执行了");
  15. c = -10;
  16. }
  17. }

以上代码段,在try和catch中都有return语句。

finally中的代码始终会执行,但是针对这种情况,他的执行时机:

先执行return语句,此时,将需要返回的值缓存起来。然后再去执行finally语句中的代码,执行结束后,返回刚才缓存的那个值。

4.5.2方式二:声明抛出异常

如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。

在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。

  1. public void readFile(String file) throws FileNotFoundException {
  2. ……
  3. // 读文件的操作可能产生FileNotFoundException类型的异常
  4. FileInputStream fis = new FileInputStream(file);
  5. ..……
  6. }

重写方法不能抛出比被重写方法范围更大的异常类型。在多态的情况下,对methodA()方法的调用-异常的捕获按父类声明的异常处理。

  1. public class A {
  2. public void methodA() throws IOException {
  3. ……
  4. } }
  5. public class B1 extends A {
  6. public void methodA() throws FileNotFoundException {
  7. ……
  8. } }
  9. public class B2 extends A {
  10. public void methodA() throws Exception { //报错
  11. ……
  12. } }

测试:

  1. class ArrayTools{
  2. //通过给定的数组,返回给定的索引对应的元素值。
  3. public static int getElement(int[] arr,int index) {
  4. if(arr==null){
  5. throw new NullPointerException("arr指向的数组不存在");
  6. }
  7. if(index<0 || index>=arr.length){
  8. throw new ArrayIndexOutOfBoundsException("错误索引,"+index+"索引在数组中不存在");
  9. }
  10. int element = arr[index];
  11. return element;
  12. }
  13. }
  14. class ExceptionDemo {
  15. public static void main(String[] args) {
  16. int[] arr = {34,12,67}; //创建数组
  17. int num = ArrayTools.getElement(null,2);// 调用方法,获取数组中指定索引处元素
  18. System.out.println("num="+num);//打印获取到的元素值
  19. }
  20. }

4.5.3 异常类中常用方法

在Throwable类中为我们提供了很多操作异常对象的方法,常用的如下:

getMessage方法:返回该异常的详细信息字符串,即异常提示信息
toString方法:返回该异常的名称与详细信息字符串
printStackTrace:在控制台输出该异常的名称与详细信息字符串、异常出现的代码位置

4.6 自定义异常

  1. NullPointerException异常类源代码:
  2. public class NullPointerException extends RuntimeException {
  3. public NullPointerException() {
  4. super();//调用父类构造方法
  5. }
  6. public NullPointerException(String s) {
  7. super(s);//调用父类具有异常信息的构造方法
  8. }
  9. }

定义异常类的格式:

  1. 格式:
  2. Class 异常名 extends Exception{ //或继承RuntimeException
  3. public 异常名(){
  4. }
  5. public 异常名(String s){
  6. super(s);
  7. }
  8. }

系统给我们提供了很多的异常类,但是这些异常类并不能够满足我们所有的需求。这种情况下,我们就需要去自定义异常。继承自异常类,写一个子类即可。

  1. //自定义异常类
  2. class NoAgeException extends Exception{
  3. NoAgeException() {
  4. super();
  5. }
  6. NoAgeException(String message) {
  7. super(message);
  8. }
  9. }
  10. //异常类的使用
  11. class Person{
  12. private String name;
  13. private int age;
  14. Person(String name,int age) throws NoAgeException {
  15. //加入逻辑判断。
  16. if(age<0 || age>200) {
  17. throw new NoAgeException(age+",年龄值非法");
  18. }
  19. this.name = name;
  20. this.age = age;
  21. }S
  22. public String toString() {
  23. return "Person[name="+name+",age="+age+"]";
  24. }
  25. }
  26. //测试
  27. class ExceptionDemo{
  28. public static void main(String[] args) {
  29. try {
  30. Person p = new Person("xiaoming",20);
  31. System.out.println(p);
  32. }
  33. catch (NoAgeException ex){
  34. System.out.println("年龄异常");
  35. }
  36. System.out.println("over");
  37. }
  38. }