java 异常体系详细介绍

一.异常概述与异常体系结构

异常:在Java语言中,将程序执行中发生的不正常情况称为“异常”。(开发过程中的语法错误和逻辑错误不是异常)。
在这里插入图片描述
Java把异常当作对象来处理,并定义一个基类java.lang.Throwable作为所有异常的超类,有两个子类Error和Exception,分别表示错误和异常。

Java程序在执行过程中所发生的异常事件可分为两类:

  • ErrorJava虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError(堆栈溢出)和OOM(OutOfMemoryError内存用完)。一般不编写针对性的代码进行处理。
  • Exception:其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如:空指针异常、数组角标越界。

java中的Exception类的子类不仅仅只是像上图所示只包含IOException和RuntimeException这两大类,Exception的子类很多很多,主要可概括为运行时异常(RuntimeException)非运行时异常,也称为不检查异常(Unchecked Exception)检查异常(Checked Exception)

  • 运行时异常:都是RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundsException等, 这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
  • 非运行时异常:是RuntimeException以外的异常,编译期间可以检查到的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。 如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。

二.try-catch-finally

Java提供的是异常处理的抓抛模型

Java程序的执行过程中如出现异常,会生成一个异常类对象,该异常对象将被提交给Java运行时系统,这个过程称为抛出(throw)异常

1.异常对象的生成

由虚拟机自动生成:程序运行过程中,虚拟机检测到程序发生了问题,如果在当前代码中没有找到相应的处理程序,就会在后台自动创建一个对应异常类的实例对象并抛出——自动抛出

由开发人员手动创建:Exception exception = new ClassCastException();——创建好的异常对象不抛出对程序没有任何影响,和创建一个普通对象一样

2.异常抛出机制

若执行try块的过程中没有发生异常,则跳过catch子句。若是出现异常,try块中剩余语句不再执行。开始逐步检查catch块,判断catch块的异常类实例是否是捕获的异常类型。匹配后执行相应的catch块中的代码。

如果异常没有在当前的方法中被捕获,就会被传递给该方法的调用者。如果异常没有在调用者方法中处理,它继续被抛给这个调用方法的上层方法。这个过程一直重复,直到异常被捕获或被传给main方法(交给JVM来捕获),这一过程称为捕获(catch)异常

如果一个异常回到main()方法,并且main()也不处理,则程序运行终止

程序员通常只能处理Exception,而对Error无能为力。

3.异常处理机制

异常处理是通过try-catch-finally语句实现的。

try{
	...... //可能产生异常的代码
}
catch( ExceptionName1 e ){
	...... //当产生ExceptionName1型异常时的处置措施
}
catch( ExceptionName2 e ){
	...... //当产生ExceptionName2型异常时的处置措施
}
[ finally{
	...... //无论是否发生异常, 都无条件执行的语句
} ]
  • try

    捕获异常的第一步是用try{…}语句块选定捕获异常的范围,将可能出现异常的代码放在try语句块中。

  • catch( Exception e)

    在catch语句块中是对异常对象进行处理的代码。每个try语句块可以伴随一个或多个catch语句。对于try里面发生的异常,他会根据发生的异常和catch里面的进行匹配(按照catch块从上往下匹配),如果有匹配的catch,它就会忽略掉这个catch后面所有的catch。

    在catch中可以捕获异常的有关信息:

    • e.getMessage():获取异常信息,返回字符串
    • e.printStackTrace():获取异常类名和异常信息及异常出现在程序中的位置。返回值void
  • finally

    不论在try代码块中是否发生了异常事件,catch语句是否执行,catch语句是否有异常,catch语句中是否有return,finally块中的语句都会被执行;

    finally中的return、throw会覆盖try、catch中的return、throw;

    finally语句和catch语句是任选的。

    public int test() {
        try {
            int a = 1;
            a = a / 0;
            return a;
        } catch (Exception e) {
            return -1;
        } finally{
            return -2;
        }
    }
    
    // 方法返回值:-2
    

    当执行代码a = a / 0;时发生异常,try块中它之后的代码便不再执行,而是直接执行catch中代码;在catch块中,当在执行return -1前,先会执行finally块;由于finally块中有return语句,因此catch中的return将会被覆盖,直接执行fianlly中的return -2后程序结束。因此输出结果是-2。

  • 注意

    • try、catch、finally三个语句块均不能单独使用,三者可以组成 try...catch...finally、try...catch、try...finally三种结构,catch语句可以有一个或多个,finally语句最多一个
    • try、catch、finally三个代码块中变量的作用域为代码块内部,分别独立而不能相互访问。如果要在三个块中都可以访问,则需要将变量定义到这些块的外面
    • 多个catch块时候,最多只会匹配其中一个异常类且只会执行该catch块代码,而不会再执行其它的catch块,且匹配catch语句的顺序为从上到下,也可能所有的catch都没执行
    • 先Catch子类异常再Catch父类异常

三.throws

throws关键字用于方法体外部的方法声明部分,用来声明方法可能会抛出某些异常类型,也可以是它的父类。仅当抛出了检查异常,该方法的调用者才必须处理或者重新抛出该异常。当方法的调用者无力处理该异常的时候,应该继续抛出。

import java.io.*;
public class ThrowsTest {
    public static void main(String[] args) {
        ThrowsTest t = new ThrowsTest();
        try {
        	t.readFile();
        } catch (IOException e) {
        	e.printStackTrace();
        }
    }
    public void readFile() throws IOException {
        FileInputStream in = new FileInputStream("data.txt");
        int b;
        while ((b=in.read()) != -1) {
            System.out.print((char) b);
        }
        in.close();
    }
}

重写方法不能抛出比被重写方法范围更大的异常类型 (<=)。在多态的情况下,对methodA()方法的调用-异常的捕获按父类声明的异常处理。

public class A {
    public void methodA() throws IOException {
    ……
	}
}
public class B1 extends A {
    public void methodA() throws FileNotFoundException {
    ……
	} 
}
public class B2 extends A {
    public void methodA() throws Exception { // 报错
    ……
	} 
}

四.throw

throw关键字是用于方法体内部,用来抛出一个Throwable类型或其子类的异常。

如果抛出了检查异常,则还应该在方法头部声明方法可能抛出的异常类型。该方法的调用者也必须检查处理抛出的异常。如果所有方法都层层上抛获取的异常,最终JVM会进行处理,处理也很简单,就是打印异常消息和堆栈信息。

public static void test() throws Exception  
{  
   throw new Exception("方法test中的Exception");  
}  

throw和throws关键字的区别

  • 写法上 : throw 在方法体内使用,throws 函数名后或者参数列表后方法体前
  • 意义 : throw 强调动作,而throws 表示一种倾向、可能但不一定实际发生
  • throws 后面跟的是异常类,可以一个,可以多个,多个用逗号隔开。throw 后跟的是异常对象,或者异常对象的引用。
  • throws 用户抛出异常,在当前方法中抛出异常后,当前方法执行结束(throws 后,如果有finally语句的话,会执行到finally语句后再结束。)。可以理解成return一样

五.自定义异常类

一般地,用户自定义异常类都是RuntimeException的子类。

  • 自定义异常类通常需要编写几个重载的构造器。
  • 自定义异常需要提供serialVersionUID
  • 自定义的异常通过throw抛出。
  • 自定义异常最重要的是异常类的名字,当异常出现时,可以根据名字判断异常类型。

语法:

// 1.定义异常类
class  自定义异常类 extends 异常类型(Exception) {
    // 2.重载构造方法
    
    // 3.将异常信息传递给父类:super(...)

}

示例:

// 备注: 这些方法怎么来的? 重写父类Exception的方法
public class CustomException extends Exception {
    static final long serialVersionUID = 13465653435L;
    
    //无参构造方法
    public CustomException(){
        super();
    }

    //有参的构造方法
    public CustomException(String message){
        super(message);
    }

    // 用指定的详细信息和原因构造一个新的异常
    public CustomException(String message, Throwable cause){
        super(message,cause);
    }

    //用指定原因构造一个新的异常
     public CustomException(Throwable cause) {
         super(cause);
     }
}

总结: 异常处理的5个关键字

捕获异常:

  • try:执行可能产生异常的代码
  • catch:捕获异常
  • finally:无论是否发生异常,代码总被执行

抛出异常:

  • throw:异常的生成阶段:手动抛出异常对象

声明异常:

  • throws:异常的处理方式:声明方法可能要抛出的各种异常类
posted @ 2019-12-13 19:57  zlgSmile  阅读(766)  评论(0编辑  收藏  举报