Java异常详解

来源:互联网 发布:windows自带图片编辑器 编辑:程序博客网 时间:2024/06/05 21:57

异常的介绍

Java中的异常就是那些会阻碍当前程序运行,使程序执行可能失败的一些可能情况,如程序中出现除零错误,数组下标越界等。异常在Java中被封装成了一个类,继承自Throwable,名为Exception,它有很多子类,分别描述了系统中很多常见的异常情况,这些异常机制的出现使得编写程序时对一些问题的处理变得尤为方便,下面是一些简单的使用情况。

异常捕获的一般格式

/** * javac ExceptionDemo.javac * java ExceptionDemo * 输出:String index out of range: 3 */class ExceptionDemo {    public static void main(String[] args) {        try {            // 可能产生异常的代码放到try内            System.out.println("Hi".charAt(3));        } catch(Exception e) {            // 对异常的处理            System.out.println(e.getMessage());        }    }}

当将catch中的Exception换成StringIndexOutOfBoundsException时,输出结果同样为String index out of range: 3,如下:这里ExceptionStringIndexOutOfBoundsException的父类,子类的对象赋值给父类的类型,这在Java中称作多态。

/** * 输出:String index out of range: 3 */class ExceptionDemo {    public static void main(String[] args) {        try {            // 可能产生异常的代码放到try内            System.out.println("Hi".charAt(3));        } catch(StringIndexOutOfBoundsException e) {            // 对异常的处理            System.out.println(e.getMessage());        }    }}

异常的继承关系

Exception的继承关系图可以清楚的知道Exception的父类及其子类的关系,超类是ThrowableErrorException的共同超类),Throwable的超类便是Object(除本身外Java中所有类的直接或者间接超类)。以Exception结尾的类都是继承自Exception

Exception的继承关系图

try内有多条语句时,可能会产生多种异常,下列代码虽然加上了异常捕获操作但是没有还是产生了异常,程序崩溃。

/** * 输出:H *       Exception in thread "main" java.lang.ArithmeticException: / by zero *       at ExceptionDemo3.main(ExceptionDemo3.java:10) */class ExceptionDemo3 {    public static void main(String[] args) {        try {            // 可能产生异常的代码放到try内            System.out.println("Hi".charAt(0));            System.out.println(123 / 0);        } catch(StringIndexOutOfBoundsException e) {            // 对异常的处理            System.out.println(e.getMessage());        }    }}

原因是虽然有catch来捕获异常,但是仅仅捕获的是StringIndexOutOfBoundsException异常,这条异常只有System.out.println("Hi".charAt(0));才会产生。而System.out.println(123 / 0);这条语句会产生另一种异常,叫做ArithmeticException,即运算异常。而这条异常没有相应的捕获语句,所以虚拟机采用默认处理方式,即让程序崩溃。

加上ArithmeticException 异常捕获后便可以正常处理异常,如下:

/** * 输出:H *       异常:/ by zero */class ExceptionDemo3 {    public static void main(String[] args) {        try {            // 可能产生异常的代码放到try内            System.out.println("Hi".charAt(0));            System.out.println(123 / 0);        } catch(StringIndexOutOfBoundsException e) {            // 对异常的处理            System.out.println(e.getMessage());        } catch(ArithmeticException e) {            System.out.println("异常:" + e.getMessage());        }    }}

难道try内有大量语句时,会产生很多异常的情况就要加很多个catch?当然你可以像第一个示例那样使用Exception来接收所有异常,但是这样又会有问题,那就是所有异常都会统一处理,那么就使用Exception和其他异常混合使用的情况,这种情况的时候要注意一点,Exception一定要放在最后面一条catch中,否则编译会报错。正确写法如下:

/** * 输出:H *       异常:/ by zero */class ExceptionDemo3 {    public static void main(String[] args) {        try {            // 可能产生异常的代码放到try内            System.out.println("Hi".charAt(0));            System.out.println(123 / 0);        } catch(StringIndexOutOfBoundsException e) {            // 对异常的处理            System.out.println(e.getMessage());        } catch(ArithmeticException e) {            System.out.println("异常:" + e.getMessage());        } catch(Exception e) {            System.out.println(e.getMessage());        }     }}

finally关键字

Java异常捕获中的另一个关键字finally,同catch的用法类似,不过finally后没有类型,finally的功能是作为try...catch...finally中必然执行的一段。也就是说try内的代码产生或者没有产生异常,最终都会执行finally内的代码。这可以应用到一些网络操作中,如访问数据库时有打开数据库,当操作数据库时出现了错误,那么在finally中写上关闭数据库的操作便起到了很好的作用。避免系统打开很多数据库连接而无法关闭且又无法操作,这样会非常消耗系统资源。访问网络时也是同样的道理。一些finally的演示如下:

/** * 输出: * B * C       */class ExceptionDemo4 {    public static void main(String[] args) {        try {            int num = 4 / 0; // 制作异常            System.out.println("A");        } catch (Exception e) {            System.out.println("B");        } finally {            System.out.println("C");        }    }}/** * 输出: * A * B * C       */class ExceptionDemo4 {    public static void main(String[] args) {        try {            System.out.println("A");            int num = 4 / 0; // 制作异常        } catch (Exception e) {            System.out.println("B");        } finally {            System.out.println("C");        }    }}/** * 输出: * 4 * 4 * 0       */class ExceptionDemo4 {    public static void main(String[] args) {        int num = 4;        try {            System.out.println(num);            int n = 10 / 0;  // 制造异常            num += 2;   // 异常发生后会立即进入异常处理部分        } catch (Exception e) {            System.out.println(num);            num = 0;        } finally {            System.out.println(num);        }    }}

try内的代码在执行时碰到了异常后便不再继续执行,而是跳到对应的异常处理代码段执行,然后再执行finally段的代码。

在带有返回值的函数中时,finally的执行如下:

/** * 输出: * try:4 * catch:4 * finally:5 * main:4      */class ExceptionDemo5 {    public static void main(String[] args) {        System.out.println("main:" + method());    }    public static int method() {        int num = 4;        try {            System.out.println("try:" + num);            int n = 10 / 0;  // 制造异常        } catch (Exception e) {            System.out.println("catch:" + num);            return num;        } finally {            num ++;            System.out.println("finally:" + num);        }        return 0;    }}

finally之前出现了return语句时,返回值的内容会被压栈,所以在finally中修改num的值是不会影响最终在main函数中接收到的返回值的内容的,这也体现了finally一定会执行的一点。

但是当遇到下面这种情况就要另当别论了:

import java.io.File;import java.io.FileOutputStream;import java.io.IOException;/** * 文件内容为:Hello * 若将System.exit(0);注释掉,文件内容为Hi */class ExceptionDemo5 {    public static void main(String[] args) {        try {            FileOutputStream fout = new FileOutputStream(new File("E:\\ex.txt"));            fout.write("Hello".getBytes());            fout.close();            System.exit(0); // 直接退出系统        } catch (IOException e) {        } finally {            try {                FileOutputStream fout = new FileOutputStream(new File("E:\\ex.txt"));                fout.write("Hi".getBytes());                fout.close();            } catch (IOException e) {            }        }    }}

所以finally的内容必然执行也是要建立再程序还处于正在运行的状态,程序已退出虚拟机当然无法再执行finally的内容了。

try...finally组合

除了try...catch...finally还有一种try...finally组合方式,即去掉catch段。若代码段抛出的是RuntimeException便将异常抛给上一层,若是非RuntimeException或其子类便会编译错误。(ArithmeticException属于RuntimeException

/** * 输出: * method:finally * main:/ by zero */class ExceptionDemo6 {    public static void main(String[] args) {        try {            method();        } catch(Exception e) {            System.out.println("main:" + e.getMessage());        }    }    public static void method() {        try {            int num = 6 / 0;        } finally {            System.out.println("method:finally");        }    }}

异常的抛出throws

对于异常的处理可以使用try...catch,可以使用try...catch...finally,也可以使用try..finally,当然还有其他方式,你可以使用throws把异常抛给上一层,这里的上一层指的是如main函数调用method方法,对于method方法来说main函数就是上一层。

/** * 输出: * main:Hello */class ExceptionDemo7 {    public static void main(String[] args) {        try {            method();        } catch(ClassNotFoundException e) {            System.out.println("main:" + e.getMessage());        }    }    public static void method() throws ClassNotFoundException {        Class<?> c = Class.forName("Hello");    }}

多个异常的抛出时可以在throws后面使用,隔开,而抛出一条异常是使用throw来实现的,如下:

/** * 输出: * main:-Message- */class ExceptionDemo7 {    public static void main(String[] args) {        try {            method();        } catch(ClassNotFoundException e) {            System.out.println("main:" + e.getMessage());        } catch(Exception e) {            System.out.println("main:" + e.getMessage());        }    }    public static void method() throws ClassNotFoundException, IllegalAccessException {        // Class<?> c = Class.forName("Hello");        throw(new IllegalAccessException("-Message-"));    }}

自定义异常

自定义异常其实很简单,只需要继承Exception即可,或者继承Exception的子类,如下:

/** * 输出: * main:My Exception */class MyException extends Exception {    public MyException(String msg) {        super(msg);    }}class ExceptionDemo7 {    public static void main(String[] args) {        try {            method();        } catch(MyException e) {            System.out.println("main:" + e.getMessage());        }    }    public static void method() throws MyException {        throw(new MyException("My Exception"));    }}

RuntimeException简介

当抛出RuntimeException或者RuntimeException的子类时无需使用throws在方法名后面标出,这一类异常可能无法通过捕获处理很好地处理,如除零错误发生时即使捕获到了异常,但是后面的运算结果是会受到影响的,一定会出现一个不正确的运算结果。如上面出现过的除零异常,无需在方法名后加异常类型说明:

/** * 输出: * method:finally * main:/ by zero */class ExceptionDemo6 {    public static void main(String[] args) {        try {            method();        } catch(Exception e) {            System.out.println("main:" + e.getMessage());        }    }    public static void method() /*这里不需要加 ArithmeticException*/{        try {            int num = 6 / 0;        } finally {            System.out.println("method:finally");        }    }}
1 0
原创粉丝点击