• java项目中异常处理情况


    一,基本概念

      异常是程序在运行时出现的不正常情况。是Java按照面向对象的思想将问题进行对象封装。这样就方便于操作问题以及处理问题。
      异常处理的目的是提高程序的健壮性。你可以在catch和finally代码块中给程序一个修正机会,使得程序不因不可控制的异常而影响程序的流程。同时,通过获取Java异常信息,也为程序的开发维护提供了方便。
    Java异常类层次结构图


    Java中的异常用对象来处理,并定义java.lang.Throwable作为所有异常的超类。Throwable分成了两个不同的分支,Exception(异常)和 Error(错误);

    其中异常类Exception又分为运行时异常(RuntimeException)和非运行时异常。或不受检查异常(Unchecked Exception)和检查异常(Checked Exception);

    异常是针对 方法 来说的,抛出、声明抛出、捕获和处理异常都是在方法中进行的;

    Java异常处理通过5个关键字try、catch、throw、throws、finally进行管理;

    Error(错误):灾难性的致命的错误,是程序无法控制和处理的。

      Error类对象由 Java 虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关。例如,Java虚拟机运行错误、内存溢出。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止;还有发生在虚拟机试图执行应用时,如类定义错误、链接错误。这些错误是不可查的,因为它们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况。对于设计合理的应用程序来说,即使确实发生了错误,本质上也不应该试图去处理它所引起的异常状况。
    Exception(异常):通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这些异常。。

    运行时异常和受检查异常

    运行时异常 (unChecked异常):

      RuntimeException类及其子类都被称为运行时异常。这些异常一般是由程序逻辑错误引起的,属于应该解决的Bug,程序应该从逻辑角度避免这类异常的发生,不推荐try-catch来捕获处理,但是有时候为了增强用户体验,保证Crash次数降到最低,会人为捕捉一些运行时异常。这种异常的特点是Java编译器不去检查它,也就是说,当程序中可能出现这类异常时,即使没有用try-catch语句捕获它,也没有用throws字句声明抛出它,还是会编译通过。但在运行时会被系统自动抛出。
    非运行时异常 (checked异常):

      除了RuntimeException类及其子类外,其他的Exception类及其子类都属于非运行时异常,从程序语法角度讲是必须进行处理的异常,如果不处理程序就不能编译通过。
    异常转型和异常链:

      我们做的JEE项目时候,一般会有三层的结构:持久层、逻辑层、展现层。异常也是如此的,当我们各个层之间传递异常,我们就需要先封装,然后传递。
    异常链示例

    catch (SQLException e)
    {
    throw new JdbcException(e);
    }

    二、异常处理机制

      在 Java 应用程序中,异常处理机制为:抛出异常,捕捉异常。
    抛出异常
      当一个方法出现错误引发异常时,方法创建异常对象并交付运行时系统,异常对象中包含了异常类型和异常出现时的程序状态等异常信息。运行时系统负责寻找处置异常的代码并执行。
      该方法的调用者必须处理或者重新抛出该异常。当方法的调用者无力处理该异常的时候,应该继续抛出,所经方法都层层上抛获取的异常,若最终都没有被处理,将交由虚拟机处理。处理也很简单,就是打印异常消息和堆栈信息,记录日志。
    捕捉异常
      在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(exception handler)。潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适的异常处理器。
      运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。当运行时系统遍历调用栈而未找到合适的异常处理器,如果出现异常的线程为主线程,则整个程序运行终止;如果非主线程,则终止该线程,其他线程继续运行。
      在方法中用try-catch语句捕获并处理异常,catach语句可以有多个,用来匹配处理异常。并且尽量将捕获底层异常类的catch子句放在前面。
      异常总是先被抛出,后被捕捉的。
    Java规定
      对于可查异常必须捕捉、或者声明抛出。允许忽略不可查的RuntimeException和Error。
      RuntimeException由业务逻辑保证。
    3.1、抛出异常实例(throws 和 throw)

    public class Throws
     {
     public static void main(String[] args) throws Exception
     {//抛出异常类
     System.out.println(10 / 0);
     throw new Exception("抛出异常对象");
     //System.out.println("throw后面的代码不再执行");
     }
    }


    3.2、捕获异常实例(try-catch 和 finally)

    import java.util.InputMismatchException;
    import java.util.Scanner;
    
    public class TryCatch {
    public static void main(String[] args) {
    Scanner input = new Scanner(System.in);
    
    try { //可能会发生异常的程序代码
    System.out.print("请输入一个数字:");
    int a = input.nextInt();
    } catch (InputMismatchException e) {// 捕捉异常
    System.err.println("数据类型不符!");
    e.printStackTrace();
    System.err.println(e.getMessage());
    // return; 若捕捉到异常会先执行finally, 再return。
    } catch (Exception e) {// catch 先写子类,再写父类
    System.out.println("再捕捉一次!");
    // System.exit(0);
    } finally {// 除非执行System.exit(0),否则都会执行
    System.out.println("finally 被执行!");
    // 应用举例:确保关闭数据库,关闭流
    }
    
    System.out.println("我还是被执行了!");
    // 如果提前return,则不执行了
    }
    }

    throw 和throws关键字的区别
    throw用于抛出异常对象,后面跟的是异常对象;throw用在方法内。
    throws用于抛出异常类,后面跟的异常类名,可以跟多个,用逗号隔开。throws用在方法方法签名上。
    通常情况:方法内容如果有throw,抛出异常对象,并没有进行处理,那么方法上一定要声明,否则编译失败。
    三、Java常见异常

    4.1、Error

    LinkageError:链接错误;
    ThreadDeath:线程死锁;
    OutOfMemoryError:内存溢出;
    StackOverflowError :堆栈溢出;
    NoClassDefFoundError:类定义错误;
    Virtual MachineError:虚拟机运行错误。
    4.2、运行时异常(unChecked异常)

    SecurityException:安全性异常;
    NullPointerException:空指针异常;
    ClassCastException:类型强制转换异常;
    ClassNotFoundException:找不到类异常;
    IllegalArgumentException:非法参数异常;
    NegativeArraySizeException:数组长度为负异常;
    ArithmeticException:算术条件异常。如:整数除零;
    ArrayIndexOutOfBoundsException:数组下标越界异常;
    ArrayStoreException:数组中包含不兼容的值抛出的异常;
    StringIndexOutOfBoundsException:字符串下标越界异常;
    ArrayStoreException:向数组中存放与声明类型不兼容对象异常;
    4.3、非运行时异常(checked异常)

    IOException:输入输出流异常;
    SQLException:数据库操作异常;
    EOFException:文件已结束异常;
    TimeoutException:执行超时异常;
    DataFormatException:数据格式化异常;
    NoSuchFieldException:没有匹配的属性异常;
    ClassNotFoundException:没有匹配的类异常;
    FileNotFoundException:没有匹配的文件异常;
    NoSuchMethodException:没有匹配的方法异常;
    4.4、Throwable类的主要方法

    public String getMessage():返回关于发生的异常的详细信息。这个消息在Throwable 类的构造函数中初始化了。
    public Throwable getCause():返回一个Throwable 对象代表异常原因。
    public String toString():使用getMessage()的结果返回类的串级名字。
    public void printStackTrace():打印toString()结果和栈层次到System.err,即错误输出流。
    public StackTraceElement [] getStackTrace():返回一个包含堆栈层次的数组。下标为0的元素代表栈顶,最后一个元素代表方法调用堆栈的栈底。
    public Throwable fillInStackTrace():用当前的调用栈层次填充Throwable 对象栈层次,添加到栈层次任何先前信息中。
    四、自定义异常实例

    实例一:

    class UserException extends Exception { // 继承父类
    public UserException() {
    super();
    }
    public UserException(String message) {
    super(message);
    }
    }
    
    public void activatioin(String code) throws UserException {
    try {
    User user = userDao.findByCode(code);
    if(user == null) throw new UserException("无效的激活码!");
    if(user.isStatus()) throw new UserException("您已经激活过了");
    userDao.updateStatus(user.getUid(), true); // 修改状态
    } catch(SQLException e) {
    throw new RuntimeException(e);
    }
    }

    实例二:

    package Test; 
    import java.lang.Exception; 
    public class TestException { 
    static int quotient(int x, int y) throws MyException { // 定义方法抛出异常 
    if (y < 0) { // 判断参数是否小于0 
    throw new MyException("除数不能是负数"); // 异常信息 
    } 
    return x/y; // 返回值 
    } 
    public static void main(String args[]) { // 主方法 
    int a =3; 
    int b =0; 
    try { // try语句包含可能发生异常的语句 
    int result = quotient(a, b); // 调用方法quotient() 
    } catch (MyException e) { // 处理自定义异常 
    System.out.println(e.getMessage()); // 输出异常信息 
    } catch (ArithmeticException e) { // 处理ArithmeticException异常 
    System.out.println("除数不能为0"); // 输出提示信息 
    } catch (Exception e) { // 处理其他异常 
    System.out.println("程序发生了其他的异常"); // 输出提示信息 
    } 
    }
    
    } 
    class MyException extends Exception { // 创建自定义异常类 
    String message; // 定义String类型变量 
    public MyException(String ErrorMessagr) { // 父类方法 
    message = ErrorMessagr; 
    }
    
    public String getMessage() { // 覆盖getMessage()方法 
    return message; 
    } 
    } 


    五、Java异常处理的原则和技巧

    不要把自己能处理的异常抛给别人;
    catch块尽量保持一个块捕获一类异常;
    细化异常的类型,不要不管什么类型的异常都写成Excetpion;
    避免过大的try块,不要把不会出现异常的代码放到try块里面;

    如果把父类的异常放到前面,后面的catch语句块将得不到执行的机会;

    尽量将异常统一抛给上层调用者,由上层调用者统一决定如何进行处理。
    不要用try-catch参与控制程序流程,异常控制的根本目的是处理程序的非正常情况;
    只要不是retry或者queue的情况,基本上所有的异常都是需要继续向上抛的,最终交给顶层异常处理机制(应用或者容器)。

    Java异常处理三原则

    具体明确

    提早抛出

    通过提早抛出异常(又称"迅速失败"),异常得以清晰又准确。堆栈信息立即反映出什么出了错(提供了非法参数值),为什么出错(文件名不能为空值),以及哪里出的错(readPreferences()的前部分)。
    延迟捕获

    异常发生时,不应立即捕获,而是应该考虑当前作用域是否有有能力处理这一异常的能力,如果没有,则应将该异常继续向上抛出,交由更上层的作用域来处理。
    如何记录异常(写入日志)

    在异常最开始发生的地方进行日志信息记录;
    如果捕获到一个异常,但是这个异常是可以处理的。则无须记录异常;
    捕获到一个未记录过的异常或外部系统异常时,应该记录异常的详细信息。
    记录checked异常还是unChecked异常

    如果一个异常是可以恢复的,可以被调用者正确处理的,使用checked异常;
    如果一个异常是致命的,不可恢复的。或者调用者去捕获它没有任何益处,使用unChecked异常;
    在使用unChecked异常时,必须在在方法声明中详细的说明该方法可能会抛出的unChekced异常。由调用者自己去决定是否捕获unChecked异常;
    受检异常尽可能转化为非受检异常。


    在类继承的时候,方法覆盖时如何进行异常处理

    如果父类的方法声明一个异常,则子类在重时声明的异常范围应该不小于 父类;
    如果父类或者接口中的方法没有抛出过异常,那么子类是不可以抛出异常的,如果子类的覆盖的方法中出现了异常,只能try不能throws;
    如果这个异常子类无法处理,已经影响了子类方法的具体运算,这时可以在子类方法中,通过throw抛出RuntimeException异常或者其子类,这样,子类的方法上是不需要throws声明的。

  • 相关阅读:
    关于一个标准的poi下载文件模板 可拿来来直接使用
    关于史上最简单的导出(导出excal)
    关于spring MVC的面试题
    关于响应式布局(pc端访问自适应 手机端访问自适应)
    密码生成器
    关于poi的导出(POI)
    关于MUI一个很实用的前端框架
    (转载)插画版Kubernetes指南(小孩子也能看懂的kubernetes教程)
    (转载)Python tips: 什么是*args和**kwargs?
    python编程中的if __name__ == 'main': 的作用和原理
  • 原文地址:https://www.cnblogs.com/lq13035130506/p/9942149.html
Copyright © 2020-2023  润新知