前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java中的异常处理

Java中的异常处理

作者头像
roobtyan
发布2019-02-21 15:50:55
1.3K0
发布2019-02-21 15:50:55
举报
文章被收录于专栏:最高权限比特流

异常概述

Java的基本设计思想是“Badly formed code will not be run!”。这句话的大致意思是:错误形式的代码不会被运行。 我们在写代码的时候,提升错误恢复能力是提升代码健壮的重要措施。而“为了创建一个更加健壮的系统,那么每一个组件都必须是健壮的”。从而,在Java中出现了异常处理机制。 不像C语言,基本处理错误的代码都是程序员写上去的,而在Java中,除非是要自己自定义异常的时候,我们一般都是通过异常处理代码块来解决问题的。不但提高了代码的健壮性,还提高了代码的可读性。 那么,异常处理的定义是什么呢?当程序运行时出现了异常(不是错误),可能是空指针异常等等很多异常,能够对当前出现异常的代码进行处理,或是直接报告异常,或是将异常抛给特定的位置进行决断处理。 同大多数的需求一样,异常处理也被设计者设计成了一个类:Throwable。在这个类的下面,又有Error(错误)、和Exception(异常)。Error(错误)一般情况下不会通过代码进行处理,因为一般能报错误的情况,都是十分严重的情况,大多数错误都是由JVM(Java虚拟机)引起的。例如下面的代码:

代码语言:javascript
复制
byte[] buf = new byte[1024*1024*1024];
System.out.println(buf);

执行这段代码,肯定是会报错的。原因如下: JVM默认情况下只管理了64M的内存,而我们的程序需要的是1G的内存,很显然已经超出了管理范围(内存溢出),所以会报错。 在Exception(异常)类下,又有RunTimException(运行时异常)以及费运行时异常。这里的“……类下”指的是继承关系。 下面,我会逐个类介绍,并且会附上相应的代码供大家参考。

Throwable类

基本方法 1. toString() 输出该异常的类名 2. getMessage() 输出异常的信息,需要通过构造方法传入异常信息,也就是new一个对象的时候传入的参数(手动滑稽) 3. printStackTrace() 打印栈信息。 代码片段如下:

代码语言:javascript
复制
Throwable able = new Throwable("恶心!!!");
System.out.println(able.toString()); // 输出该异常的类名
System.out.println(able.getMessage()); // 输出异常的信息
able.printStackTrace(); // 打印栈信息

运行结果如下:

那么,我们来看一个出现异常的例子: 这个函数传入的参数的y值可能是0,程序会出现异常并停止

代码语言:javascript
复制
public static void div(int x, int y) {
System.out.println(x / y);
System.out.println("除法运算");
}

那么对于这种情况,我们应该如何进行处理呢?这就正式引入了我们要讨论的话题,异常处理的方式。首先我们来介绍第一种。

1.try{//可能发生异常的代码 }catch(异常类 变量名){//处理}。

我们以上面那个除法运算函数作为最基本的例子。当我们没有进行异常处理的时候,程序遇到问题时会停止。进行了异常处理时,程序还会继续执行,并且会按照我们给出的格式进行报错。

代码语言:javascript
复制
try{
    div(10,0);
}catch(Throwable t){
        System.out.println(t.toString());
        System.out.println(t.getMessage());
        t.printStackTrace();
        System.out.println();
        System.out.println("除数不能为0");
}

程序运行的结果如下图所示:

那么,一个异常的处理解决了,我们该如何进行多个异常的处理呢?

多个异常的处理 为了实现多个异常的处理情况,这里我们使用最简单的方法:设定一个数组。代码如下:

代码语言:javascript
复制
public class Exception {
    public static void main(String[] args) {
        int[] arr = new int[] {1,2};
        div(1,0,arr);
    }
    public static void div(int i,int j,int arr[]) {
        try {
            System.out.println(arr[1]);
            System.out.println(i / j);
        }catch(ArithmeticException e) {
            e.toString();
            e.getMessage();
            e.printStackTrace();
            System.out.println("******算术异常*******");
        }catch(ArrayIndexOutOfBoundsException e) {
            e.toString();
            e.getMessage();
            e.printStackTrace();
            System.out.println("******数组角标越界*******");
        }catch(NullPointerException e) {
            e.toString();
            e.getMessage();
            e.printStackTrace();
            System.out.println("******空指针异常*******");
        }
    }
}

所以,当执行上述代码的时候,将会出现算数异常,也就是ArithmeticException。 如果将main函数中的代码换成这个样子:

代码语言:javascript
复制
public void main(String[] args){
    int[] arr = new int[] {1,2};
    arr = null;
    div(1,0,arr);
}

这时将会出现空指针异常,也就是NullPointerException ,原因很简单,我们已经将arr数组置为null,所以访问的时候肯定是会出现空指针异常的。 如果我们再将div中的代码改成下面这个样子:

代码语言:javascript
复制
System.out.println(arr[3]);

数组中一共有两个值,0、1,我们访问的是3,很显然是数组角标越界,也就是ArrayIndexOutOfBoundsException 。 总结 1. 程序中可能有多个语句发生异常,可以同时放在try中。如果某条语句发生异常的时候,程序将会对catch中的异常进行匹配,如果能够匹配上,则执行相应的catch中的代码,如果没有匹配上,程序停止。 2. 如果程序中真的出现了多个异常,则只会执行try代码片段中的第一个出现异常的语句的异常处理语句,剩余的异常不会再处理。 使用多态进行异常处理 什么是多态呢?我们之前肯定学过,简单来讲,就是“用父类的引用指向子类对象”,我简单解释一下,看下面的代码:

代码语言:javascript
复制
Father  f = new Son();

在这里,Son类是继承与Father类的,所以用Father的引用f来指向Son类的对象就是这个意思了,如果还不明白的话,就再好好看看前面的内容。 具体操作代码如下:

代码语言:javascript
复制
try {
        System.out.println(arr[1]); // 数组越界
        System.out.println(x / y); // 除零
        Son s = (Son) f; // 类型转换
    } catch (Exception e) {
        e.toString();
        e.getMessage();
        e.printStackTrace();
        System.out.println("出现错误");
    }

上面的代码很简单,就是说当无论出现什么错误的时候,都只用一个Exception来匹配,我们知道,其实各种各样的异常都是继承于Exception类的,所以可以用Exception的引用指向具体的异常对象,如NullPointerExeception等。 多个catch语句之间的执行顺序 1. 按照顺序执行,从上到下 2. 如果catch的异常有继承关系,则:当子类异常在上,父类异常在下的时候,编译正常。也就是说,如果NullPointerException在上,Exception在下的时候,满足这一条;当子类异常在下,父类异常在上的时候,比如NullPointerException在下,Exception在上,那么就会编译报错,因为子类异常无法catch到。所以,多个异常需要使用子类父类的顺序进行使用。 注意:处理异常应该catch异常具体的子类,可以处理的更具体,不要为了简化代码使用异常的父类。

抛出处理

除了try……catch这种方法进行异常处理外,我们还可以使用抛出处理异常。 看下面的这个方法:

代码语言:javascript
复制
public static void div(int x, int y) throws Exception { // 声明异常,通知方法调用者。
    if (y == 0) {
        throw new Exception("除数为0"); // throw关键字后面接受的是具体的异常的对象
    }
    System.out.println(x / y);
    System.out.println("除法运算");
}

上面的例子就是抛出处理,使用throw关键字,注意,在方法中使用的是throw,而在方法名上使用的是throws关键字,这个很好理解,有点类似于英文中的复数,一个方法中抛出的异常很有可能不止一个,所以使用throws方法。

代码语言:javascript
复制
public static void main(String[] args) {
        try {
            div(2, 0);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("over");
    }

调用了div方法后,我们需要对这个方法抛出的异常进行处理,这又涉及到了try……catch块。如果不对抛出的异常进行处理,那么编译不会通过。在main函数上依然可以即系抛出,这样就是交给了JVM进行处理了,肯定是不推荐这样做的。 throw和throws的区别 1. 相同:都是用于做异常的抛出处理的。 2. 不同点: 使用的位置: throws 使用在函数上,throw使用在函数内 后面接受的内容的个数不同: throws 后跟的是异常类,可以跟多个,用逗号隔开。 throw 后跟异常对象。

自定义异常

当现有异常体系中的异常无法满足我们的需求的时候,我们就需要自定义异常。 根据上面的介绍,我们知道,所有的异常都是继承于父类Exception,所以,我们自定义异常也是继承Exception就可以了。

代码语言:javascript
复制
class NoRiceException extends Exception {……}

这个Exception是什么意思呢? 字面意思就是没有米饭的异常,嘿嘿。 我们去食堂买饭,就说要买米饭(Rice),然后卖饭的阿姨告诉你,米饭卖没了,这就是NoRiceException,没有米饭的异常,这个异常在现有的异常体系中肯定是没有的。 下面再来介绍一个大头:

运行时异常和非运行时异常

  1. RuntimeException(运行时异常) RuntimeException的子类有:
    • ClassCastException————–多态中,可以使用Instanceof 判断,进行规避
    • ArithmeticException————–进行if判断,如果除数为0,进行return
    • NullPointerException————-进行if判断,是否为null
    • ArrayIndexOutOfBoundsException————使用数组length属性,避免越界 这些异常时可以通过程序员的良好编程习惯进行避免的
  2. 非运行时异常(受检异常) 如果出现了非运行时异常必须进行处理throw或者try{}catch(){}处理,否则编译器报错。
    • IOException 使用要导入包import java.io.IOException;
    • ClassNotFoundException
    • Sun 的API文档中的函数上声明异常,那么该异常是非运行是异常, 调用者必须处理。
    • 自定义异常一般情况下声明为非运行时异常

总结 1. 子类覆盖父类方法时,父类方法抛出异常,子类的覆盖方法可以不抛出异常,或者抛出父类方法的异常,或者该父类方法异常的子类。 2. 父类方法抛出了多个异常,子类覆盖方法时,只能抛出父类异常的子集 3. 父类没有抛出异常子类不可抛出异常 4. 子类发生非运行时异常,需要进行try{}catch的(){}处理,不能抛出。 5. 子类不能比父类抛出更多的异常

接下来我们来看异常处理的最后一部分,finally

finally

我们知道,当程序出现异常的时候,经过异常处理,程序会停止执行,所以,在处理完异常以后,后续的代码将不会执行,这样肯定是有一定问题的,比如,我们调用了资源,需要关闭这个资源的时候,程序停止了,资源却仍然被占用着,这样是不是很有问题啊。为了处理这样的问题,就要使用finally块。 finally就是必须执行的代码。 1. 出现问题的情况: try{ // 可能发生异常的代码 } catch( 异常类的类型 e ){ // 当发生指定异常的时候的处理代码 }catch… 比较适合用于专门的处理异常的代码,不适合释放资源的代码。 2. 实现方式一: try{ } catch(){} finally{ // 释放资源的代码 } finally块是程序在正常情况下或异常情况下都会运行的。 比较适合用于既要处理异常又有资源释放的代码 3. 实现方式二 try{ }finally{ // 释放资源 } 比较适合处理的都是运行时异常且有资源释放的代码。 4. finally:关键字主要用于释放系统资源。 - 在处理异常的时候该语句块只能有一个。 - 无论程序正常还是异常,都执行finally。 5. finally是否永远都执行? - 只有一种情况,但是如果JVM退出了System.exit(0),finally就不执行。 - return都不能停止finally的执行过程。 以上,就是异常处理的全部内容。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018年02月01日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 异常概述
  • Throwable类
    • 1.try{//可能发生异常的代码 }catch(异常类 变量名){//处理}。
      • 抛出处理
        • 自定义异常
          • 运行时异常和非运行时异常
            • finally
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档