全面理解java异常机制

      在理想状态下,程序会按照我们预想的步骤一步一步的执行,但是即使你是大牛,你也不可避免出错,所以java为我们提供了异常机制。本文将会从以下几个方面介绍java中的异常机制:

  • 异常机制的层次结构
  • 异常的处理过程
  • 抛出异常
  • 捕获异常
  • 异常机制的实现细节

一、异常机制的层次结构       在java程序设计语言中,所有的异常对象都是派生于Throwable类,一般情况下,如果java中内置的异常类不能满足需求,可以自定义异常类只需要继承与Throwable类即可,下面是java 中的异常层次结构:

      error类表示java内部错误,例如jvm出错或者内存不足等,这层一般不用我们关心(其实我们也无可奈何,如果出现此种问题),一旦出现此种错误程序会自动结束。       我们主要还是需要研究Exception类及其子类,Exception主要有两个子类,IOException和RuntimeException我们一个个看。IOException是IO错误类问题导致的异常,是可预知的,例如由于文件不存在而打开文件失败引起的异常等。RuntimeException类异常表示运行时异常,例如数组下标越界,访问空指针等,是不可预知的,但是这类异常于编写程序时应当予以避免,例如你可以使用if(a==null).....,加以检查避免。       以上从一种角度对这些主要的异常类进行了分类,其实我们还有一种分类方式,按照是否是检查类异常(checked)进行分类,什么是检查类异常?检查类异常(checked)就是指编译器会检查当前的代码块中,判断是否有这么一条语句在程序执行时可能产生异常,如果有就会建议程序员处理。(实际上你必须处理,不然编译不会通过),在以上的分层中,error类和RuntimeException类属于非检查异常类(unchecked),而IOException属于检查类异常。 二、异常的处理过程

public class Test2 {
    public static void main(String[] args){
        doMaths();
    }
    public static void doMaths(){
        int a = 10/0;//此处将会出现异常
        System.out.println(a);
    }
}
输出结果:
Exception in thread "main" java.lang.ArithmeticException: / by zero
    at test.Test2.doMaths(Test2.java:8)
    at test.Test2.main(Test2.java:5

      相信大家在看完上文之后,可以判断出此处的异常应该属于RuntimeException,属于unchecked异常,由输出的结果可以看出:main方法中调用方法doMaths();,于是进入该方法内部,执行int a = 10/0;产生异常,在本方法中未找到处理,于是返回调用处(即main函数中),发现main函数中依然没有处理方法,于是执行默认操作(打印异常信息)。       我们的程序难免会出现问题,但是绝对不能让用户看出来你的程序出现问题,所以这些异常信息是绝对不能让用户看见的,且不说他是否能看懂,一旦遇到“行家”,你的程序就会存在安全隐患,由此可见,对异常的处理是多么重要。下面我们将讨论两种处理异常的机制。 三、抛出异常       以下所讨论的处理异常的方法,主要还是针对检查类异常(checked),因为对于error类的错误我们没法处理,对于RuntimeException类异常我们应当尽量在程序中加以避免,所以我们主要还是对检查类异常进行处理。       我们使用关键字throws将本程序中可能遇到的异常向上抛出(向调用出抛出,让调用者处理),而本身不做处理。

public void test() throws AppException{
    ................
    ...............
}

      throws 紧跟方法的括号后面,这个叫异常的声明表示本方法不处理这个异常,谁调用我这个方法谁来处理(后面将讨论如何处理异常,因为总要有人来处理,否则就默认打印异常信息),可以声明多个异常,异常之间使用逗号相隔。       那么throw关键字和throws关键字究竟有什么联系呢?throw可以理解为是在throws关键字的主动行为。先看代码:

public class Test2 {
    public static void main(String[] args){
        show();
    }
    public static void show(){
        String s = null;
        s.charAt(0);
    }
}
/*以上代码抛出NullPointerException,空指针异常*/
public static void show() throws NullPointerException{
        String s = null;
        s.charAt(0);
    }
/*你可以选择抛给调用者*/
public static void show(){
        String s = null;
        if(s==null){
            throw new NullPointerException();
        }
        s.charAt(0);
    }
/但是如果你可以判断s处可能出现异常,就可以主动抛出异常*/

      小结一下,throws关键字表示:本函数中存在某个异常但是我不知道,如果出现此异常就抛给调用者。throw一般和判断语句if配合的多表示:如果满足条件就结束此函数并向调用者抛出异常,否则就继续执行接下来的代码。 四、捕获异常       以上说了那么多,我们知道如何将遇到的异常抛出,让别人处理,但是总是有人需要处理这个异常的,总不能让系统默认的将异常信息打印出来吧!再者说,一个strong的程序必须要有对错误的处理,让程序控制在手中而不是莫名卡死或者退出。接下来,我们一起探讨如何捕获异常对他进行处理。       捕获异常,我们使用try{}catch{}关键字,try中代码表示可能出现异常的代码块,catch中的代码块表示捕捉到该异常之后需要进行的后续操作。

/*使用上述代码,添加try catch捕获异常*/
public class Test2 {
    public static void main(String[] args){
        try{
            show();//可能出现异常的代码块
        }catch(NullPointerException z){
            System.out.println("throw Exception");
            //捕捉到异常后的操作
        }
    }
    public static void show()throws NullPointerException {
        //将异常抛给调用者
        String s = null;
        s.charAt(0);
    }
}
输出结果:
throw Exception

      对比于之前,打印自己控制的内容是不是比默认打印异常信息更加的友好?我们常常会遇到这么一个问题:打开一个文件,准备对文件进行操作,但是在对文件的操作过程中出现异常,退出程序了,但是文件并没有被显式的关闭,造成资源浪费。       我们常常使用关键字finally来完成类似这样的操作。finally关键字紧跟catch后面,表示无论是否发生异常都会执行的代码块。finally不是必须的可以没有。

try{
  //可能出现异常的代码块
}catch{
  //捕获异常之后的代码块
}finally{
  //肯定要执行的代码块
}

五、异常机制的实现细节       第一个我想说的是try{}catch{}这种结构并不是唯一的,可以由多个catch语句的,对多个不同的异常进行捕获。       第二个很重要的finally这个关键字中如果出现return等关键字程序的接下来的执行顺序问题。例如:

try{
  int a=0;
  //出现异常
  return a;
}catch(..){
 a=1;
 return a;
}finally{
 a=2;
 return a; 
}
程序返回:2;

      能想明白么?其实不难,只要知道finally语句块中是最后执行的就可以理解了,当try中出现异常,被catch接住,将a赋值为1,在返回之前跳转到finally语句块中,最后在finally语句块中返回2,这种在finally语句块中有return语句的会阻止catch中的return执行。       所以,我们建议finally语句块中不要使用return语句。 以上便是java异常机制的基本内容,如果错误,望大家指出。tk

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏linux驱动个人学习

内存对齐的三条原则

1:数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员...

2874
来自专栏java学习

重要通知!小编出新的Java练习题咯!!

正确答案 3月5号公布 一、选择题和问答题 1、在一个java原文件中,import, class, package语句的顺序是( )。 A. import ...

3475
来自专栏java一日一条

Java到底是不是一种纯面向对象语言?

Java——是否确实的 “纯面向对象”?让我们深入到Java的世界,试图来证实它。

321
来自专栏云霄雨霁

Java--违例控制(异常处理)

854
来自专栏微信公众号:Java团长

Java知识点集锦

答:不是。Java中的基本数据类型只有8个:byte、short、int、long、float、double、char、boolean;除了基本类型(primi...

811
来自专栏编程思想之异常处理

Java编程思想之通过异常处理错误

1.     异常分为被检查的异常和运行时异常,被检查的异常在编译时被强制要求检查。异常被用来错误报告和错误恢复,但很大一部分都是用作错误报告的。

441
来自专栏Golang语言社区

Golang语言捕获panic异常并转化为error

package mainimport ( "fmt" "errors")func testPanic2Error() (err erro...

3175
来自专栏大数据

Zzreal的大数据笔记-ScalaDay02

昨天整理了一下Scala的一些基本内容,不是很全面,不过作为学习Spark的基础足够了, 如果需要系统的学习Scala,建议还是去菜鸟教程一步步的看下来会比较条...

17610
来自专栏成猿之路

Java面试题基础篇

986
来自专栏从流域到海域

《Java程序设计基础》 第8章手记Part 2

第八章内容 Part 2 - … - 抽象类和抽象方法 - 接口及接口的实现 - 利用接口实现类的多重继承 - 内部库和匿名类 ...

1929

扫码关注云+社区