zoukankan      html  css  js  c++  java
  • Java编程基础-异常

    一、异常

    1、什么是异常

            在java中,程序在运行时出现的不正常情况称为异常,以异常类的形式对这些非正常情况进行封装,通过异常处理机制对程序运行时发生的各种问题进行处理。其实就是java对不正常情况进行描述后的对象体现。


    2、java异常类 

             在Java中提供了大量的异常类,查阅API文档,Throwable 类是 Java 语言中所有错误或异常的超类。只有当对象是此类(或其子类之一)的实例时,才能通过 Java 虚拟机或者 Java throw 语句抛出。类似地,只有此类或其子类之一才可以是 catch 子句中的参数类型。

            Throwable有两个直接的子类Error和Exception,其中,Error代表程序中产生的错误,Exception代表程序中产生的异常。

           a)    Error类称为错误类,它表示java运行时产生的系统内部错误或资源耗尽的错误,是比较严重的,仅靠修改程序本身是不能恢复执行的。对于Error一般不编写针对性的代码对其进行处理。
           b)    Exception类称为异常类,它表示程序本身可以处理的错误,在java开发程序中进行的异常处理,都是针对Exception类及其子类。在Exception类的众多子类中有一个特殊的RuntimeException类,该类及其子类用于表示运行时异常,除了此类,Exception类下所有其它的子类都用于表示编译时异常。对于Exception可以采用针对性的代码对其进行处理。


    3、Throwable常用方法

    String getMessage()   :返回此throwable的详细消息字符串

    void printStackTrace()   :将此throwable及其追踪输出至标准错误流
    void printStackTrace(PrintStream s)   :将此throwable及其追踪输出到指定的输出流


    4、常见异常

    RuntimeException子类:

    a) java.lang.ArrayIndexOutOfBoundsException

        数组下标越界异常。当对数组的索引值为负数或大于等于数组大小时抛出。

    b)java.lang.ArithmeticException
        算术条件异常。比如:整数除零等。

    c)java.lang.NullPointerException
        空指针异常。当应用试图在要求使用对象的地方使用了null时,抛出该异常。比如:调用null对象的实例方法、访问null对象的属性、计算null对象的长度、使用throw语句抛出null等等
     d)java.lang.ClassNotFoundException
        找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历CLASSPAH之后找不到对应名称的class文件时,抛出该异常。
    e)java.lang.NegativeArraySizeException  数组长度为负异常

     f)java.lang.IllegalArgumentException 非法参数异常

    IOException

    a)IOException:操作输入流和输出流时可能出现的异常。
    b)FileNotFoundException   文件未找到异常

    其他

    a)ClassCastException    类型转换异常类
    b)SQLException   操作数据库异常类
    c)NoSuchFieldException   字段未找到异常
    d)NoSuchMethodException   方法未找到抛出的异常
    e)NumberFormatException    字符串转换为数字抛出的异常
    f)StringIndexOutOfBoundsException 字符串索引超出范围抛出的异常
    g)IllegalAccessException  不允许访问某类异常



    二、运行时异常和编译时异常

            异常可以分为编译时被检测异常(该异常在编译时,如果没有处理(没有抛也没有try),编译失败。)和编译时不被检测的异常(运行时异常RuntimeException及其子类。在编译时,不需要处理,编译器不检查。)

     1、编译时异常(也称checked异常)

            编译时异常的特点是java编译器会对其进行检查,如果出现异常就必须对异常进行处理,否则程序无法通过编译。处理方式有两种:一是使用try..catch语句对异常进行捕获;二是使用throws关键字声明抛出异常,让调用者对其处理。

    2、运行时异常(也称unchecked异常)

            特点是java编译器不会对其进行检查,当程序中出现这类异常时,即时没有捕获或抛出处理,编译也能通过。运行时异常一般是由于程序中的逻辑错误引起的,在程序运行时无法恢复。



    三、异常处理机制

    在java应用程序中,对异常的处理要么抛出异常,要么捕获异常。

    捕获异常 

    1、 在Java中,异常通过try-catch语句捕获。其一般语法形式为:

           try {
                // 可能发生异常的程序代码
            } catch (ExceptionType1 e) {
                // 捕获并处置try抛出的异常类型Type1
            } catch (ExceptionType2 e) {
                // 捕获并处置try抛出的异常类型Type2
            }


             异常捕获处理过程:关键词try后的一对大括号将一块可能发生异常的代码包起来,称为监控区域。Java方法在运行过程中出现异常,则创建异常对象。将异常抛出监控区域之外,由Java运行时系统试图寻找匹配的catch子句以捕获异常。catch语句带一个Throwable类型的参数, 表示可捕获异常类型。当try中出现异常时,catch会捕获到发生的异常,并和自己的异常类型匹配, 若匹配,则执行catch块中代码,并将catch块参数指向所抛的异常对象。catch语句可以有多个, 用来匹配多个中的一个异常,一旦匹配上后则运行其异常处理代码,try-catch语句结束。
    匹配的原则是:如果抛出的异常对象属于catch子句的异常类,或者属于该异常类的子类,则认为生成的异常对象与catch块捕获的异常类型相匹配。


    2、示例演示:

    数学上我们都知道除法中被除数不能为0,我们以此为例。

    首先,演示不作任何处理时的情况。

    1. public class TestException {  
    2.   
    3.     public static void main(String[] args) {  
    4.         int x = 5;  
    5.         int y = 0;// 除数为0  
    6.         System.out.println(x / y);  
    7.   
    8.     }  
    9. }  

    运行结果为:

    Exception in thread "main" java.lang.ArithmeticException: / by zero
    at test.TestException.main(TestException.java:8)

    系统报了个ArithmeticException: / by zero 算术异常,并且指出是被0除


    其次:我们演示捕捉throw语句抛出的除数为0的异常并处理。

    1. public class TestException {  
    2.   
    3.     public static void main(String[] args) {  
    4.         int x = 5;  
    5.         int y = 0;// 除数为0  
    6.         try {//监控区域  
    7.             if (y == 0) {  
    8.                 throw new ArithmeticException();  
    9.                 //通过throw语句抛出异常  
    10.             }  
    11.             System.out.println(x / y);  
    12.         } catch (ArithmeticException e) {//捕捉异常  
    13.             System.out.println("程序出现异常,除数不能为0");  
    14.         }  
    15.     }  
    16. }  

    运行结果为:

    程序出现异常,除数不能为0


            简单分析:在try监控区域通过if语句进行判断,当“除数为0”的错误条件成立时引发ArithmeticException异常,创建 ArithmeticException异常对象,并由throw语句将异常抛给Java运行时系统,由系统寻找匹配的异常处理器catch并运行相应异常处理代码,打印输出“程序出现异常,除数不能为0”,try-catch语句结束,继续程序流程。实际上,“除数为0”等ArithmeticException,是RuntimException的子类。而运行时异常将由运行时系统自动抛出,不需要使用throw语句。


    最后,我们测试捕捉运行时系统自动抛出“除数为0”引发的ArithmeticException异常。

    1. public class TestException {  
    2.   
    3.     public static void main(String[] args) {  
    4.         int x = 5;  
    5.         int y = 0;// 除数为0  
    6.         try {  
    7.             System.out.println(x / y);  
    8.         } catch (ArithmeticException e) {//捕捉异常  
    9.             System.out.println("程序出现异常,除数不能为0");  
    10.         }  
    11.     }  
    12. }  

    运行结果为:

    程序出现异常,除数不能为0


            简单分析:在运行中出现“除数为0”错误,引发ArithmeticException异常。运行时系统创建异常对象并抛出监控区域,转而匹配合适的异常处理器catch,并执行相应的异常处理代码。由于检查运行时异常的代价远大于捕捉异常所带来的益处,运行时异常不可查。Java编译器允许忽略运行时异常,一个方法可以既不捕捉,也不声明抛出运行时异常。


            注意:一旦某个catch捕获到匹配的异常类型,将进入异常处理代码。一经处理结束,就意味着整个try-catch语句结束。其他的catch子句不再有匹配和捕获异常类型的机会。而对于有多个catch子句的异常程序而言,应该尽量将捕获的子类异常的catch子句放在前面,同时尽量将捕获相对高层的父类异常的catch子句放在后面。否则,捕获底层的子类异常的catch子句将可能会被屏蔽。
    RuntimeException异常类包括运行时各种常见的异常,RuntimeException异常类的catch子句应该放在最后面,否则可能会屏蔽其后的特定异常处理或引起编译错误。



    3、try-catch语句还可以包括第三部分,就是finally子句。它表示无论是否出现异常,都应当执行的内容。

    try-catch-finally语句的一般语法形式为:
           try {
                // 可能发生异常的程序代码
            } catch (ExceptionType1 e) {
                // 捕获并处置try抛出的异常类型Type1
            } catch (ExceptionType2 e) {
                // 捕获并处置try抛出的异常类型Type2
            }finally{
                //无论是否发生异常,都将执行的语句块
            }

           finally 块:无论是否捕获或处理异常,finally块里的语句都会被执行。当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。在以下4种特殊情况下,finally块不会被执行:
    1)在finally语句块中发生了异常。
    2)在前面的代码中用了System.exit()退出程序。
    3)程序所在的线程死亡。
    4)关闭CPU。

    代码示例:

    1. public class TestException {  
    2.   
    3.     public static void main(String[] args) {  
    4.         try {  
    5.             int result = divide(4, 0);// 调用divide()方法  
    6.             // int result = divide(4, 2);//测试未发生异常情况  
    7.             System.out.println(result);  
    8.         } catch (Exception e) {  
    9.             System.out.println("捕获的异常信息为" + e.getMessage());  
    10.         } finally {  
    11.             System.out.println("进入finally代码块");  
    12.         }  
    13.         System.out.println("程序继续向下执行");  
    14.     }  
    15.   
    16.     public static int divide(int x, int y)// 定义一个两个整数相除的方法  
    17.     {  
    18.         int result = x / y;// 定义一个变量result记住相除的结果  
    19.         return result;// 将结果返回  
    20.     }  
    21. }  

    运行结果为:

    捕获的异常信息为/ by zero
    进入finally代码块
    程序继续向下执行

    未发生异常测试结果为:

    2
    进入finally代码块
    程序继续向下执行

    try、catch、finally三个语句块应注意的问题: 
    a)try、catch、finally三个语句块均不能单独使用,三者可以组成 try...catch...finally、try...catch、 try...finally三种结构,catch语句可以有一个或多个,finally语句最多一个。 

    b)try、catch、finally三个代码块中变量的作用域为代码块内部,分别独立而不能相互访问。 如果要在三个块中都可以访问,则需要将变量定义到这些块的外面。 
    c)多个catch块时候,只会匹配其中一个异常类并执行catch块代码,而不会再执行别的catch块, 并且匹配catch语句的顺序是由上到下。

    d)必须遵循语句块放置顺序try-catch-finally的顺序。

    e)可嵌套 try-catch-finally 结构。比如,try中还可以有try..catch处理


    抛出异常

    4、throws抛出异常

           如果一个方法可能会出现异常,但没有能力处理这种异常,可以在方法声明处用throws子句来声明抛出异常。例如汽车在运行时可能会出现故障,汽车本身没办法处理这个故障,那就让开车的人来处理。throws语句用在方法定义时声明该方法要抛出的异常类型,如果抛出的是Exception异常类型,则该方法被声明为抛出所有的异常。多个异常可使用逗号分割。throws语句的语法格式为:

    methodName([param1,param2,..])throws ExceptionType1[,ExceptionType2..]{}
            throws关键字需要写在方法声明的后面,throws后面需要声明方法中发生异常的类型,通常将这种做法称为方法声明抛出一个异常。 使用throws关键字将异常抛给调用者后,如果调用者不想处理该异常,可以继续向上抛出,但最终要有能够处理该异常的调用者。

    代码示例:

    1. public class TestException {  
    2.   
    3.     public static void main(String[] args) {  
    4.         try {  
    5.             int result = divide(4, 0);  
    6.             System.out.println(result);  
    7.         } catch (ArithmeticException e) {  
    8.             System.out.println("捕获的异常信息为" + e.getMessage());  
    9.         } finally {  
    10.             System.out.println("进入finally代码块");  
    11.         }  
    12.         System.out.println("程序继续向下执行");  
    13.     }  
    14.   
    15.     public static int divide(int x, int y) throws ArithmeticException// 抛出异常  
    16.     {  
    17.         int result = x / y;  
    18.         return result;  
    19.     }  
    20. }  

    throws抛出异常规则:

    a) 如果是运行时异常,那么可以不使用throws关键字来声明要抛出的异常,编译仍能顺利通过,但在运行时会被系统抛出。
    b)如果是编译时异常,要么用try-catch语句捕获,要么用throws子句声明将它抛出,否则会导致编译错误

    c)仅当抛出了异常,该方法的调用者才必须处理或者重新抛出该异常。当方法的调用者无力处理该异常的时候,应该继续抛出。


    5、使用throw抛出异常

            throw总是出现在函数体内,用来抛出一个Throwable类型的异常。程序会在throw语句后立即终止,它后面的语句执行不到,然后在包含它的所有try块中(可能在上层调用函数中)从里向外寻找含有与其匹配的catch子句的try块。 由于异常是异常类的实例对象,我们可以创建异常类的实例对象通过throw语句抛出。该语句的语法格式为:
    throw new ExceptionType();

    代码示例:

    1. public class MyException extends Exception {// 创建自定义异常类  
    2.     private String message;// 定义String类型变量  
    3.     private int value;// 定义int类型变量  
    4.   
    5.     public MyException() {  
    6.         super();  
    7.     }  
    8.   
    9.     public MyException(String message, int value) {  
    10.         this.message = message;  
    11.         this.value = value;  
    12.     }  
    13.   
    14.     public int getValue() {  
    15.         return value;  
    16.     }  
    17.   
    18.     public String getMessage() {  
    19.         return message;  
    20.     }  
    21. }  

    测试:

    1. public class TestMyException {  
    2.   
    3.     public static void main(String[] args) {  
    4.   
    5.         try {// 包含可能发生异常的语句  
    6.             int result = divide(4, -1);// 调用divide()方法  
    7.             System.out.println(result);  
    8.         } catch (MyException e) {// 处理自定义异常  
    9.             System.out.println(e.getMessage());// 输出异常信息  
    10.             System.out.println("错误的负数是:" + e.getValue());// 输出异常值  
    11.         } catch (ArithmeticException e) { // 处理ArithmeticException异常  
    12.             System.out.println("除数不能为0"); // 输出提示信息  
    13.         } catch (Exception e) { // 处理其他异常  
    14.             System.out.println("程序发生了其他的异常"); // 输出提示信息  
    15.         }  
    16.         System.out.println("over");  
    17.     }  
    18.   
    19.     public static int divide(int x, int y) throws MyException// 定义方法抛出  
    20.     {  
    21.         if (y < 0) {// 判断参数是否小于0  
    22.             throw new MyException("出现了除数是负数的情况!------/by FuShu", y);// 异常信息  
    23.         }  
    24.         return x / y;// 返回值  
    25.     }  
    26. }  

    运行结果为:

    出现了除数是负数的情况!------/by FuShu
    错误的负数是:-1
    over

    throws和throw的区别:
    a)throws使用在函数上。throw使用在函数内。
    b)throws后面跟的是异常类,可以跟多个,用逗号隔开。throw后面跟的是异常对象。


    四、自定义异常

            因为实际项目中会出现特有的问题,而这些问题并未被java所描述并封装对象。所以对于这些特有的问题可以按照java对问题的封装思想。将特有的问题,进行自定义的异常封装。在java中允许用户自定义异常,自定义异常步骤:
    (1)、创建自定义异常类,且该类必须继承自Exception或其子类。(因为异常体系有一个特点,因为异常类和异常对象都被抛出。他们都具备可抛性,这个可抛性是Throwable这个体系中的独有特点。)
    (2)、在方法中通过throw关键字抛出异常对象。
    (3)、如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理;否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作。
    (4)、在出现异常方法的调用者中捕获并处理异常。

    自定义异常可参见上面使用throw抛出异常的代码示例。

     异常在子父类覆盖中的体现:
    (1)、子类在覆盖父类时,如果父类的抛出方法异常,那么子类的覆盖方法,只能抛出父类的异常或该异常的子类
    (2)、如果父类方法中抛出多个异常,那么子类在覆盖该方法时,只能抛出父类异常的子集。
    (3)、如果父类或接口的方法中没有异常抛出,那么子类在覆盖方法时,也不可以抛出异常。如果子类方法发生了异常,就必须要进行try处理,绝对不能抛。

     
  • 相关阅读:
    【FZYZOJ】数论课堂 题解(约数个数定理)
    【FZYZOJ】无向图的联通图个数 题解(组合数学)
    【BalticOI2003】Gem 题解(树形DP)
    中国剩余定理 学习笔记
    同余 学习笔记
    树形DP 学习笔记(树形DP、树的直径、树的重心)
    【USACO02FEB】Rebuilding Roads 重建道路 题解(树形DP)
    JavaSE 基础 第02节 搭建Java开发环境
    JavaSE 基础 第01节 Java语言介绍
    eclipse 调节字体大小
  • 原文地址:https://www.cnblogs.com/stsinghua/p/6418508.html
Copyright © 2011-2022 走看看