前面各小节中所见到的各种异常都来自于Java基础类库。如果基础类库中所定义的异常类还不能满足编程需要,程序员可以自己定义异常类。自定义的异常类必须是Throwable的直接或间接子类,否则编译器不认为它是异常类的家族成员,也不能用throw关键字抛出这个类的对象。实际开发中,程序员自己定义的异常类一般都会以Exception或它的子类作为父类。当自定义异常类继承了Exception或它的子类后,还可以根据需要重写父类的中的各个方法。最常被重写的方法就是getMessage()方法,这个方法的返回值是一个字符串对象,它表示异常信息。自定义异常类重新定义getMessage()方法的返回值,实质上就是重新定义了异常类信息。另外,当定义好一个异常类后,还需要设定抛出这个异常的触发条件,这样虚拟机就知道在什么情况下抛出这个异常。
为方便读者深入理解自定义异常类的用法,此处举一个例子:假设我们通过setSpeed()方法为某一游戏中的人物设定设置行进速度,如果设置的行进速度超过了30公里/小时,就认为速度值不合理,并且希望在这种情况下能抛出一个异常。下面的【例07_13】就展示了如何实现这一需求。
【例07_13 自定义异常】
MyException.java
- public class MyException extends RuntimeException{
- public String getMessage() {
- return "速度超过限定值";//定义异常信息
- }
- }
Exam07_13.java
- import java.util.Scanner;
-
- public class Exam07_13 {
- static int v ;
- public static void setSpeed(int speed) {
- if(speed>30) {//抛出异常的条件
- throw new MyException();
- }else {
- v = speed;
- System.out.println("速度值为:"+v);
- }
- }
-
- public static void main(String[] args) {
- Scanner sc = new Scanner(System.in);
- int speed;
- System.out.println("请设置速度值:");
- try {
- speed = sc.nextInt();
- setSpeed(speed);
- }catch(Exception e) {
- System.out.println(e.getMessage());
- }
- }
- }
【例07_13】包含两个类,分别是:MyException和Exam07_13。其中MyException是一个自定义异常类,它用来表示用户设置的速度超过了限定值。MyException类重写了getMessage()方法,把方法的返回值定义为“速度超过限定值”,这其实就是把MyException的异常信息定义为“速度超过限定值”。 而在Exam07_13类中有一个setSpeed()方法,它用来设定游戏人物的行进速度。在setSpeed()方法中,如果经判断发现所设定的速度超过了限定值,就会用throw关键字抛出一个MyException异常对象来通知调用者。在Exam07_13的main()方法中调用了setSpeed()方法,如果用户输入的速度超过了限定值就会导致setSpeed()方法抛出异常。【例07_13】的运行结果如图7-16所示。

图7-16 【例07_13】运行结果
当catch捕获到异常对象后,调用了被MyException类重写过的getMessage()方法,因此输出结果为“速度超过限定值”。其实还可以调用异常对象的printStackTrace()方法来显示异常的类型和出现的位置,读者可以尝试自行完成这个操作。
【例07_13】中的自定义异常类MyException是RuntimeException的子类,它属于未检查异常,编译器并不会强制要求处理这个异常。如果想强制要求main()方法必须对setSpeed()方法中的异常进行处理,可以把MyException修改为Exception的子类使其成为一个已检查异常,并在定义setSpeed()方法时用throws关键字声明这个异常。
有些读者会问:设定速度值不合理的情况下,只要在控制台上输出一条提示信息就可以,为什么还要抛出异常对象?观察程序结构可以看出:Exam07_13类的main()方法调用了setSpeed()方法,在setSpeed()方法中发现设定的速度值不合理时,如果只是在控制台上输出一句提示信息,并不能改变main()方法的执行路径,也就是说:在setSpeed()方法发现速度值不合理的情况下,main()方法仍然会按照既定流程执行下去,只是在控制台上多了一条提示信息而已。但是,如果在发现速度值不合理的情况下抛出了一个异常对象,效果就会完全不同:首先,main()方法会立刻跳转到catch块中执行代码,这使得程序在出现不合理状况时能及时改变执行路径,不至于在错误的道路上继续走下去。其次,异常对象中包含了程序运行出错的具体原因、出错的位置等详细信息,抛出异常其实就是把这些信息传递给主调方法,这样主调方法就能获取到相关信息并根据这些信息对异常做出适当的处理。由此可见,抛出异常的操作并非“小题大做”。综上所述:被调方法运行出错时,如果希望能把运行出错的相关信息传递给主调方法并改变主调方法的执行路径,各位读者就可以用抛出异常的方式实现。