欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

25、异常捕获与处理

程序员文章站 2024-02-20 14:26:22
...

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

异常的产生

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

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、异常处理流程图
25、异常捕获与处理

流程描述:
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();
        }
    }
}

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

相关标签: # Java基础 java