Java 异常详解

本文深入探讨了Java中的异常处理机制,包括异常的分类、捕获与处理、异常的继承体系、常用异常类型的示例以及如何在finally块中回收资源。强调了处理异常的最佳实践,如避免过度使用异常、不要忽略捕获的异常等。同时,介绍了如何声明和抛出异常,以及自定义异常的创建。最后,讨论了异常追踪栈的概念和异常处理原则。
摘要由CSDN通过智能技术生成

异常的处理机制

当程序出现意味的情况时,系统会自动生成一个异常对象来通知程序

  1. 程序中,可以使用特定的语句捕获异常对象,读取对象中的信息,进而做出处理;
  2. 程序中,可以使用特定的语句抛出异常对象,将这个异常交给程序调用者处理
  3. 可以在捕获异常后,做出尽可能的处理,之后再向外抛出,将剩下的部分,交给程序的调用者处理

异常的继承体系

在这里插入图片描述

  • 所有的非常的情况被分为两类 错误和异常其中 错误代表虚拟机相关问题,一般无法处理,也无需处理

  • 所有的异常(exception)被分为两类 runtimeException,代表运行时异常,程序可以显示处理这种异常,也可以不处理,而是交给顶层调用者统一处理;

  • 非运行时异常(Checked异常)程序必须显示地处理这种异常,否则在编译阶段会发生错误,导致程序无法编译通过编译

捕获异常

  • try catch 语句
try{
         业务逻辑
   }catchException e){
    异常处理代码   
}
  1. 无论哪行代码发生异常,系统都会生成一个异常对象,这与try …catch…语句没有关系
  2. 若程序没有对这个异常做任何处理,则程序在此退出
  3. 创建异常对象后,jvm会寻找可以处理它的catch快,并将异常对象交给这个catch去处理
  4. 括号里的参数是Exception类型,是所有异常的父类 所以他能处理所有的异常情况。

访问异常信息

  • String getMessage() 返回该异常的详细消息字符串
    StackTraceElement[] getStackTrace() 返回该异常的跟踪栈信息
    void printStackTrace() 将该异常的跟踪栈信息打印到标准输出设备
几个常见的异常类型演示
public static void main(String[] args) {

       // process1(args);
        process2(args);
    }
//    几个常见的异常类型
//    ArrayIndexOutOfBoundsException  数组索引越界异常
//    NumberFormatException           数字格式异常
//    ArithmeticException             算术异常
//    处理异常原则 先小异常再大异常

    public static void process1(String[] args){
        try {
            int m = Integer.parseInt(args[0]);
            int n = Integer.parseInt(args[1]);
            System.out.println("m / n =" + (m / n));
    }catch (Exception e){
            System.out.println("##############################3");
            e.printStackTrace();

            System.out.println("##############################3");
            System.out.println(e.getMessage());

            System.out.println("##############################3");
            for (StackTraceElement element: e.getStackTrace()){

            }
        }
    }

    public static void process2(String[] args) {
        try {
            int m = Integer.parseInt(args[0]);
            int n = Integer.parseInt(args[1]);
            System.out.println("m / n =" + (m / n));
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("数组小标越界“;" + e.getMessage());
        }catch (NumberFormatException e ){
            System.out.println("数字格式错误“;" + e.getMessage());
        }catch (ArithmeticException e){
            System.out.println("数学运算失败2“;" + e.getMessage());
        }catch (Exception e){
            System.out.println("未知异常“;" + e.getMessage());
        }

    }
    }

回收资源

来想想把回收资源的代码放在那里耶

  • 放在 try的后面?显然不行为什么 因为放在try的后面 要是前面出现了异常 就直接去了catch里面了 回收资源代码就执行不到了
  • 放在catch里面的后面? try里面没有异常就捕获不到 就不会执行catch
  • 直接不放在try — catch 里面 放在 catch的后面 ? 如果try 有异常 不处理的话 直接打印异常 中断程序 也不行
  • 所以为了 解决这一现象 增加了一个finally代码块
    所以完整的是 try…catch…finally
finally 注意事项
  1. finally块中的代码 总是会被执行
  2. try 是必须的,catch ,finally 是可选的
  3. catch finally 这两个至少要出现一个
  4. finally 最多出现一次 但是他必须位于 try catch 的最后面
代码演示
  public static void main(String[] args) {

       // process1(args);
        process2(args);
    }
//    几个常见的异常类型
//    ArrayIndexOutOfBoundsException  数组索引越界异常
//    NumberFormatException           数字格式异常
//    ArithmeticException             算术异常
//    处理异常原则 先小异常再大异常

    public static void process1(String[] args){
        try {
            int m = Integer.parseInt(args[0]);
            int n = Integer.parseInt(args[1]);
            System.out.println("m / n =" + (m / n));
    }catch (Exception e){
            System.out.println("##############################3");
            e.printStackTrace();

            System.out.println("##############################3");
            System.out.println(e.getMessage());

            System.out.println("##############################3");
            for (StackTraceElement element: e.getStackTrace()){

            }
        }
    }

    public static void process2(String[] args) {
        try {
            int m = Integer.parseInt(args[0]);
            int n = Integer.parseInt(args[1]);

            System.out.println("m / n =" + (m / n));
            System.out.println("我被执行了");
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("数组小标越界“;" + e.getMessage());
        }catch (NumberFormatException e ){
            System.out.println("数字格式错误“;" + e.getMessage());
        }catch (ArithmeticException e){
            System.out.println("数学运算失败2“;" + e.getMessage());
        }catch (Exception e){
            System.out.println("未知异常“;" + e.getMessage());
        }finally {
            try {//try  catch 可以被嵌套
                System.out.println("FINALLY 我被执行了");
                int m = Integer.parseInt(args[0]);
            }catch (ArrayIndexOutOfBoundsException e){
                System.out.println(e.getMessage());
            }
        }
        System.out.println("出来了");
    }
    }

在这里插入图片描述

抛出异常

  • 声明抛出异常
    方法() 后面接 throws 接异常类型
  1. throws 语句用于标识某方法可以抛出的异常,它位于方法签名之后

  2. throws 语句声明抛出异常后,程序中就无需使用try语句捕获该异常了;

  3. 再重写时,子类方法声明 抛出异常类型不能比父类方法声明抛出的异常类型大

  4. 作用是 用于在程序中主动抛出一个异常

  5. throw 语句抛出的不是异常类型,而是一个异常实例

  6. 对于主动抛出的异常,也可以采用try块捕获,或者采用throw语句向外抛出由调用捕获或者处理 或者在抛出去

  7. thows用在方法声明的时候,表示这个方法可能会抛出这个异常,可以声明多种异常,由方法的调用者捕获

  8. thow用在方法体内,用于抛出一个异常实例,由方法内部的语句进行处理。


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

    public static void process1(String[] args)
      throws  ArrayIndexOutOfBoundsException,NumberFormatException,ArithmeticException{
        int m = Integer.parseInt(args[0]);
        int n = Integer.parseInt(args[1]);
        System.out.println("m / n =" + (m / n));
    }

}

自定义异常

  • 若定义Checked异常,则继承于Exception
  • 若定义Runtime异常,则继承于RuntimeException
  • 自定义的异常类,通常需要提供如下构造方法;
    public SomeException(){};
    public SomeException(String message){};
    public SomeException(String message ,Throwable cause){};
代码演示
public static void main(String[] args)  {
        try {
            String s = inputUsername();
            System.out.println(s);
        } catch (UsernameException e) {
            System.out.println(e.getMessage());
        }
    }


    public static String inputUsername() throws UsernameException {
        System.out.println("请输入账号:");
        try (
                Scanner scanner = new Scanner(in);
        ) {
            String s = scanner.nextLine();
            if (!s.matches("^\\w{6,20}$")) {//正则表达式


                throw new UsernameException("账号格式错误!");
            }
            return s;
        } catch (RuntimeException e) {
           throw new UsernameException("输入账号失败",e);
        }

    }

}
//自定义异常
class UsernameException extends Exception{
    public UsernameException() {
        super();
    }

    public UsernameException(String message) {
        super(message);
    }

    public UsernameException(String message, Throwable cause) {
        super(message, cause);
    }
}

异常追踪栈

  1. 程序运行时,经常会发生一系列方法的调用,从而形成方法调用栈
  2. 异常机制会导致异常在这些方法 之间传播,而异常传播的顺序与方法的调用相反
  3. 异常从发生异常的方法向外传播,首先传给该方法的调用者,再传给上层调用者
  4. 最终传给main方法,若依然没有得到处理,则jvm会终止程序,并打印异常追踪栈信息;
    在这里插入图片描述
异常处理的原则
  • 不要过度使用异常 不要用异常代理错误处理代码,不要用异常处理代替流程控制语句
  • 不要忽略捕获异常 对于捕获到的异常 ,要进行合适的修复,对于不能处理的部分,应该抛出新的异常
  • 不要直接捕获所有的异常 应该不同异常做出有针对性的处理,而捕获所有的异常容易压制(丢失)异常
  • 不要使用过于庞大的try代码块 庞大的代码块会导致业务过于复杂,不利于分析异常的原因,也不利于程序的阅读和维护!!!!!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值