Java异常处理机制

异常处理机制的意义

开发人员总希望自己的程序能够正常地一步一步执行下去不出错误,但是没有人敢保证自己写的程序百分百完美。不管是自己写的程序还是调用别人的程序,执行过程中可能会出现意想不到的错误。如果不考虑异常的情况,程序一旦出现异常就无法继续执行下去了。下面举个例子:

public class Test {
    public static void main(String[] args) {
        System.out.println("1. 除法运算开始了");
        System.out.println("2. 运算结果:" + 2 / 0);  // 除数为0,此时程序肯定会出错
        System.out.println("3. 除法运算结束啦");
    }
}

运行结果:

Exception in thread "main" java.lang.ArithmeticException: / by zero
    at cn.chengh.demo.Test.main(Test.java:6)
1. 除法运算开始了

程序执行到计算的那一步的时候发生异常,然后整个程序就终止运行了。程序以这种粗暴的方式结束好像不太体面,有时候我们想要即使这个程序中途出现了异常情况,程序也可以继续向下执行,最后整个程序以正常的情况结束。
好在Java提供了非常健全的异常处理机制,供开发人员处理程序可能出现的各种异常情况。
Java中有这些关键字和异常处理有关:try、catch、finally、throw、throws。
程序中处理异常的格式如下:

try {
    // try块中存放有可能出现异常的代码
} catch (异常类1 实例化对象) {
    // 处理该异常
} catch (异常类n 实例化对象) {
    // 处理该异常
} finally {
    // 不管是否出现异常,finally块中代码一定会执行
}

可以使用多个catch块来分别处理各种不同的异常
finally块非必须,可以有也可以没有

利用异常处理机制,上面的程序可以优化成这个样子:

public class Test {
    public static void main(String[] args) {
        System.out.println("1. 除法运算开始了");
        try {
            System.out.println("2. 运算结果:" + 2 / 0);  // 除数为0,此时程序肯定会出错
        } catch (ArithmeticException e) {
            System.out.println("2. 啊,运算出现异常!");
        }
        System.out.println("3. 除法运算结束啦");
    }
}

执行结果:

1. 除法运算开始了
2. 啊,运算出现异常!
3. 除法运算结束啦

哇,现在即使程序出现了异常,程序也能够完整的执行完。这就是程序需要异常处理机制的原因。

异常的种类

在Java中,Throwable类是所有异常类的父类,所有的异常类都继承自Throwable。异常分为两大类,Error类和Exception类。Error类表示在JVM中发生的错误,这类错误是无法在程序中处理的,比如OutOfMemoryError。另一大类是Exception,这类异常是开发人员在编写程序时可以处理的。Exception类下又分为两种类型的异常,RuntimeException类,称为运行时异常。其他的都称为非运行时异常。
非运行时异常要求开发人员必须要使用try catch进行处理,否则程序编译无法通过。而运行时异常不强制要求开发人员使用 try catch进行处理,如果没有使用try catch处理且在运行时发生了异常,异常会交给JVM进行默认的异常处理。举个运行时异常的例子

class Car {
    public void run() {}
}

public class Test {
    public static void main(String[] args) {
        Car car = null;
        car.run();
    }
}

该程序运行会报NullPointerException异常,但是在程序中没有使用try catch处理也是可以编译通过,因为NullPointerException是RuntimeException类型的异常。

异常处理流程

1、程序执行过程中产生异常,JVM首先会实例化出一个该异常类的对象。
2、产生了异常类对象后,判断发生异常的代码是否在try块中。如果不在try块中,则由JVM进行默认的异常处理(输出异常信息,结束程序的运行);如果在try块中,则该异常类对象会和后面的每一个catch语句的异常类型进行匹配。
3、异常类对象从上到下依次匹配catch中的异常类型。如果匹配成功,则进入该catch块执行异常处理程序。
4、不管是否匹配成功,如果后面有finally块,都会执行finally块中的程序。执行完了finally块的程序后,会根据之前异常匹配的结果决定程序接下来的执行走向。如果之前有匹配的catch块,则会继续执行finally块后面的程序,一切看起来都很正常;如果之前没有匹配的catch块,则执行完finally块程序后异常交给JVM进行默认处理(输出异常信息,结束程序的运行)。

 

关键字解释

throws关键字

throws关键字用于方法上,表示该方法中程序出现异常,则将该异常对象抛给执行该方法处去处理。

class MyUtil {
    // 方法中使用throws表示该方法出现异常会抛给调用此方法者处理
    public static int div(int a, int b) throws Exception {
        return a / b;
    }
}

public class Test {
    public static void main(String[] args) {
        // 这里调用了div方法,所以要在这里处理div可能抛出的异常
        try {
            System.out.println(MyUtil.div(10 ,0));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

throw关键字

throw用来手工抛出一个异常类实例化对象

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

自定义异常

Java本身提供的异常类有限,在实际开发项目中是不够的。有时候需要根据实际的需求定制一些异常类,Java支持开发人员自定义异常类。自定义的异常类需要继承Exception或RuntimeException类。
举个例子

class NegativeException extends Exception {
    public NegativeException(String msg) {
        super(msg);
    }
}

public class Test {
    public static void main(String[] args) {
        int result = 1 - 2;
        try {
            if (result < 0) {
                throw new NegativeException("数值为负!");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

posted @ 2019-02-24 22:23  chenghaow  阅读(284)  评论(0编辑  收藏  举报