博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java编程基础:异常处理
阅读量:4089 次
发布时间:2019-05-25

本文共 6518 字,大约阅读时间需要 21 分钟。

发表在

异常

异常定义了程序中遇到的非致命的错误,比如如程序要打开一个不存的文件、网络连接中断、除零操作、操作数越界、装载一个不存在的类等情况。

先来看看下面的程序代码:

package com.tianmaying;public class HelloWorld {        public static void main(String[] args) {        int x = 5 / 0;        System.out.println(x);    }}

编译运行上面的程序,将出现如下错误:

Exception in thread "main" java.lang.ArithmeticException: / by zero    at com.tianmaying.HelloWorld.main(HelloWorld.java:5)

上面的程序运行的结果报告发生了算术异常(ArithMethicException),应用执行提前结束,这种情况就是我们所说的异常。

try/catch语句

Java异常强制我们去考虑程序的强健性和安全性,其在设计时就考虑到这些问题,提出了一套异常处理的解决方案。将上面的程序代码进行如下修改:

package com.tianmaying;public class HelloWorld {        public static void main(String[] args) {        try {            int x = 5 / 0;            System.out.println(x);        } catch (Exception e) {            e.printStackTrace();        }        System.out.println("program is still running here!");    }}

程序运行结果如下:

java.lang.ArithmeticException: / by zero    at com.tianmaying.HelloWorld.main(HelloWorld.java:6)program is still running here!

我们将可能出现异常的代码通过try/catch代码进行了处理,当异常发生时,系统能够继续运行,而没有意外终止。

try代码块中的语句发生了异常,程序就会跳转到catch代码块中执行,执行完catch代码块中的程序代码后,系统会继续执行catch代码块之后的代码,try代码块中发生异常语句后的代码则不会再执行。比如如程序中的System.out.println(x);不会再被执行。

异常发生时,系统会将代码行号,异常类别等信息封装到一个对象中,并将这个对象传递给catch代码块,catch代码块是以下面的格式出现的。

catch(Exception e) {    e.printStackTrace();}

catch关键字后跟有一个用括号括起来的Exception类型的参数e,这跟我们经常用到的如何定义一个函数接收的参数格式是一样的。

括号中的Exception就是try代码块传递给catch代码块的变量类型,e就是变量名,所以我们也可以将e改用成别的名称(如ex),如下所示:

catch(Exception ex) {    ex.printStackTrace();}

每个try语句必须有一个或多个catch语句对应,try代码块与catch代码块及finally代码块之间不能有其他语句。

throws关键字

我们修改一下代码,将进行除法运算的代码提取到一个成员方法中:

package com.tianmaying;public class HelloWorld {        private static void fun() {        int x = 5 / 0;        System.out.println(x);    }    public static void main(String[] args) {        try {            fun();        } catch (Exception e) {            e.printStackTrace();        }        System.out.println("program is still running here!");    }}

这种情况下,我们面对的是一个fun()函数,我们如何知道需要给这个函数调用添加try/catch处理呢? 或者需要调用别人提供的方法时,我们如何知道是否需要进行异常处理呢?

在Java中,这个问题是交给被调用的方法的实现者来解决的。在这个例子中,定义fun()方法的时候,我们在方法参数列表后面增加一个throws关键字,然后增加这个方法可能抛出的异常,这种情况下调用者就必须使用try/catch进行处理了,否则编译将无法通过。

因此,我们将代码改为:

package com.tianmaying;public class HelloWorld {        private static void foo() throws Exception {        int x = 5 / 0;        System.out.println(x);    }    public static void main(String[] args) {        try {            foo();        } catch (Exception e) {            e.printStackTrace();        }        System.out.println("program is still running here!");    }}

如果此时把try/catch语句删除的话,如下代码将会报编译错误。

package com.tianmaying;public class HelloWorld {        ...    public static void main(String[] args) {        fun();        System.out.println("program is still running here!");    }}

如果一个方法中的语句执行时可能生成某种异常,但是并不能确定如何处理,则此方法应声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。也就是如果程序中的异常没有用try/catch捕捉异常以及处理异常的代码,我们可以在程序代码所在的函数(方法)声明后用throws声明该函数要抛出异常,将该异常抛出到该函数的调用函数中。

自定义异常与Throw关键字

Exception类是java.lang.Throwable类的子类。在实际应用中,我们一般是使用Exception的子类来描述特定的异常的。Exception类是所有异常类的父类,Java语言为我们提供了许多Exception类的子类,分别对应不同的异常类型,例如:

  • ArithmeticException(在算术运算中发生的异常,如除以零)
  • NullPointerException(变量还没有指向一个对象,就引用这个对象的成员)
  • ArrayIndexOutOfBoundsException(访问数组对象中不存在的元素)

使用Java内置的异常类可以描述在编程时出现的大部分异常情况。除此之外,用户还可以自定义异常。用户自定义异常类,只需继承Exception类即可。

在程序中使用自定义异常类,大体可分为以下几个步骤:

  1. 创建自定义异常类
  2. 在方法中通过throw关键字抛出异常对象1.如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理;否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作1.在出现异常方法的调用者中捕获并处理异常

比如下面的代码定义了已定义异常:

package com.tianmaying;public class BlogAppException extends Exception {    private static final long serialVersionUID = 1L;    private String command;// 可以给自定义异常增加成员变量,用以保存额外的异常信息    public BlogAppException(String command) {        this.command = command;    }    public String toString(){        return "Exception happened when executing command " + command;    }}

Java是通过throw关键字抛出异常对象,其语法格式是:

throw 异常对象;

我们来增加一个可以抛出这个异常的方法,并且在main方法中进行调用:

package com.tianmaying;public class HelloWorld {        private static void bar() throws BlogAppException {        System.out.println("let's assume BlogAppException happened when executing `create` command");        // 为了演示,这里我们假设执行create命令时,抛出了异常        throw new BlogAppException("create");    }    private static void foo() throws ArithmeticException {        int x = 5 / 0;        System.out.println(x);    }    public static void main(String[] args) {        try {            foo();            bar();        } catch (Exception e) {            e.printStackTrace();        }        System.out.println("program is still running here!");    }}

catch多个异常

Java中一个方法是可以被声明成抛出多个异常的。下面再来看看调用程序应该如何对多个异常进行处理。

有时针对不同的异常我们需要进行不同的处理,比如现在HelloWorld类的main方法中可能同时面对BlogAppExceptionArithmeticExceptioncatch (Exception e)语句不能区分到底是发生了哪个异常。

Java中可以使用一个try后面跟着多个catch来捕捉多个异常,每一个catch可以处理一个不同的异常类型。为了分别处理不同的异常情况,我们可以将代码进行如下修改:

package com.tianmaying;public class HelloWorld {    ...    public static void main(String[] args) {        try {            foo();            bar();        } catch (BlogAppException e) {            e.printStackTrace();        } catch (ArithmeticException e) {            e.printStackTrace();        } catch (Exception e) {            e.printStackTrace();        }        System.out.println("program is still running here!");    }}

如果运行时出现异常,异常处理的流程如下:

  • 首先看抛出异常是否是BlogAppException类型的异常,如果匹配则执行对应的语句块
  • 否则看抛出异常是否是ArithmeticException类型的异常,如果匹配则执行对应的语句块
  • 否则执行Exception异常对应的语句块

所以各种catch代码块的放置顺序非常重要。如果使用catch(Exception e)语句,那么它不能放在其他catch语句的前面,否则后面的catch永远得不到执行,因为Exception是所有异常的父类,可以匹配任何异常。在上面的例子中,由于只可能发生BlogAppExceptionArithmeticException,所以最后catch(Exception e)其实是不需要的。

关于异常,在继承中还要注意两点:

  • 一个方法被覆盖时,覆盖它的方法必须扔出相同的异常或异常的子类。
  • 如果父类抛出多个异常,那么重写(覆盖)方法必须扔出那些异常的一个子集,也就是说,不能扔出新的异常。

finally关键字

try/catch语句后,我们还可以有个finally语句,finally语句中的代码块不管异常是否被捕获总是要被执行的。我们将上面的程序作如下修改,来看看finally语句的用法与作用。

package com.tianmaying;public class HelloWorld {    ...    public static void main(String[] args) {        try {            foo();            bar();        } catch (BlogAppException e) {            e.printStackTrace();            return;        } catch (ArithmeticException e) {            e.printStackTrace();            return; // 即使这里return了,依然会执行finally中的语句        } catch (Exception e) {            e.printStackTrace();        } finally {            System.out.println("programe will run `finally` code block!");        }        System.out.println("program is still running here!");    }}

finally还有一个特殊之处在于,即使try代码块和catch代码块中使用了return语句退出当前方法或break跳出某个循环 ,相关的finally代码块都要执行。finally中的代码块不能被执行的唯一情况是:在被保护代码块中执行了System.exit(0)

更多文章请访问

你可能感兴趣的文章
(python版)《剑指Offer》JZ13:调整数组顺序使奇数位于偶数前面
查看>>
(python版)《剑指Offer》JZ28:数组中出现次数超过一半的数字
查看>>
(python版)《剑指Offer》JZ30:连续子数组的最大和
查看>>
(python版)《剑指Offer》JZ32:把数组排成最小的数
查看>>
(python版)《剑指Offer》JZ02:替换空格
查看>>
JSP/Servlet——MVC设计模式
查看>>
使用JSTL
查看>>
Java 8新特性:Stream API
查看>>
管理用户状态——Cookie与Session
查看>>
最受欢迎的前端框架Bootstrap 入门
查看>>
JavaScript编程简介:DOM、AJAX与Chrome调试器
查看>>
通过Maven管理项目依赖
查看>>
通过Spring Boot三分钟创建Spring Web项目
查看>>
Spring的IoC(依赖注入)原理
查看>>
Guava快速入门
查看>>
Java编程基础:static的用法
查看>>
Java编程基础:抽象类和接口
查看>>
Java编程基础:异常处理
查看>>
Java编程基础:了解面向对象
查看>>
新一代Java模板引擎Thymeleaf
查看>>