SheepDog1998

博客园 首页 新随笔 联系 订阅 管理

Java程序时会遇到的错误问题分为三种:语法错误、逻辑错误和运行时错误

异常类的继承关系

所有异常类型都是Throwable的子类,Throwable处在异常类层次结构的顶层。

它是所有异常类的父类。它有两个子类,即Error 和Exception,一般情况下很少用Throwable,而是使用它的两个子类Error、Exception 分别用来处理两组异常。

Java异常类的API:
Throwable:所有异常类的顶级父类

  Error:一般是环境出现问题时抛出的异常
    比如:内存溢出
  Exception:
    运行时异常(RunTimeException)(非检查性异常):也就是程序出bug,可以通过反复测试避免,Java并不强制提前处理它,但会给用户提示。
      比如:空指针异常 数组越界 ...
    编译时异常(IOException)(检查性异常):程序在编译过程中出现的异常,是难以避免的,不去处理相应代码则编译不通过。
      比如:FileNotFoundException....

Java中常见的异常类参见下表:

异常是如何产生的

由于Java是面向对象的语言,所以在Java语言中异常也是作为某种异常类的实例的形式出现的,某一方法中发生错误时,运行环境就创建一个异常对象,并且把它传递给系统,我们可以利用Java提供的异常处理机制,捕获这个异常对象,并且可以脱离主程序流程进行处理。我们不但可以捕获系统创建的异常实例,还可以创建自己的异常实例,并可以主动抛出异常实例,达到控制程序执行顺序的作用。

异常处理机制

自行处理:可能引发异常的语句封入在 try 块内,而处理异常的相应语句则封入在 catch 块内。

回避异常:在方法声明中包含 throws 子句,通知潜在调用者,该方法可能会出现异常,但没有处理这种异常,必须由调用者处理。

 

try/catch/finally子句

 

try{

//我们把可能会出现异常的语句放在这里。

//每次try块有异常抛出,系统会试着从上到下往每个catch块中传参,直到遇到一个类型匹配的catch块为止。

//在异常发生时,会由catch块捕获到相应的异常实例。

}catch(XXXException e){

//e就是捕获到的异常实例,它由系统自动创建,里面包含了异常的信息。

//XXXException就是Exception异常的任意子类。

//我们在这里可以对出现异常的情况进行处理,比如输出错误信息。

}

finally{

//finally语句放在try …catch语句后,fianlly语句中的代码块不管异常是否被捕获总是要执行。

//特殊情况:当try或catch代码块中执行了System.exit(0)时,finally代码块中的内容不被执行。

//finally中通常写的是关闭资源类的代码。

}

 

注意

1.catch块,是用来捕获并处理try块抛出的异常的代码块。没有try块,catch块不能单独存在。我们可以有多个catch块,以捕获不同类型的异常

2.如果程序抛出多个不同类型的异常,我们需要多个catch()语句来处理。

3.和特殊异常类相关联的catch()块必须写在和普通异常类相关联的catch()之前。

4.try{…}和catch( ){…}之间不可以添加任何代码

5.在try子句中,在发生异常的语句后面的代码将不再被执行,而是跳转到相应的catch子句中继续执行,且有多个catch语句时,其余的catch子句将不再检查或执行。

6.多个catch分支捕获的异常类型如果有父子关系,则子类在上,父类在下。如果父类在上则编译错误!

 

用户程序自定义的异常和应用程序特定的异常,必须借助于 throws 和 throw 语句来定义抛出异常。

 

throws关键字(声明异常):

 

何时使用:如果一个方法中发生了异常而没有捕获它,那么必须在这个方法头进行声明,使用throws关键字抛出异常,由方法的调用者来进行处理。

throws是应用在方法声明上,后面可以写多个异常类型,用逗号间隔,建议写一个Exception即可。

throw是应用在方法体内,用于抛出指定的某个异常对象,throws关键字抛出的异常,由调用该方法的方法处理。

用throws声明方法抛出异常,不进行处理。谁调用谁负责处理,所以被称为回避异常。

public class Example_throws {
    public void test1(int i) throws ArithmeticException {
        System.out.println(100 / i);
    }
    public void test2() {
        try {
            test1(0);
        } catch (ArithmeticException e1) {
            System.out.println(e1.toString());
        }
    }
}

在上面的例题中,方法test1()是被调用者,方法test2()是调用者,在方法test1()中可能会发生“除数为0”的异常,但是并没有对其捕获处理,而是在方法头中声明,这样调用者test2()就必须对这个可能的异常进行处理,要么捕获要么继续声明。(谁调用谁负责处理)

在Java的语法中,如果一个方法中调用了已经声明检查性异常的另一个方法,那么Java编译器会强制调用者必须处理被声明的异常,要么捕获要么继续声明。

throw语句(抛出异常):

public class ExceptionDemo {
     public static void mySqrt(int a) throws Exception {
         if (a < 0)
            throw new Exception();
         System.out.println(Math.sqrt(a));
     }
     public static void main(String args[]) {
         try {
             System.out.println("begin");
             mySqrt(25);
             mySqrt(-5);
         } catch (Exception e) {
            System.out.println("Caught e");
         }
     }
}

异常是通过关键字 throw 抛出,程序可以用throw语句引发明确的异常。

throws和throw的区分:

方法调用堆栈:后进先出

方法A调用方法B的时候,只有方法B先完成后,方法A才完成。先执行的方法总是后完成,后执行的方法先完成,类似于数据结构中的堆栈--后进先出,我们称之为方法调用堆栈。

public static void main(String[] args) {
    method1();
}

public static void method1(){
        method2();
        System.out.println("method1");
    }
    public static void method2(){
        method3();
        System.out.println("method2");
    }
    public static void method3(){
        method4();
        System.out.println("method3");
    }
    public static void method4(){
        System.out.println("method4");
    }

                /*
        结果
        method4
        method3
        method2
        method1
        */
                        

注意

1.catch子句的捕获范围,限制于与其匹配的try子句,不能捕获其他try子句中的异常。

2.try/catch/finally三个子句中变量的作用域独立而不能相互访问。如果要在三个子句中都可以访问,则需要将变量定义到这些子句的外面。

3.不要写过大的try子句,一个try子句中尽量不要存在太多的异常。

4.一个方法中如果有发生异常的可能,则可以进行捕获处理,也可以声明由调用者来处理。

5.throw语句后不允许有其他语句,因为这些语句没有机会执行。

6.不能利用异常处理来进行程序的分支处理,它只是处理非正常情况的一种机制。

 

--2021.4.13凌晨

第一次写就肝到这么晚,吐了...

 

posted on 2021-04-13 01:32  SheepDog1998  阅读(196)  评论(0)    收藏  举报