网站Logo 苏叶的belog

java异常详解

wdadwa
3
2025-12-07

一,异常概述

异常介绍:

  • 异常:代表程序出现的问题
  • 误区:不是让我们以后不出异常,而是程序出了异常之后,该怎么处理。

异常的继承体系:
2953321-20230404202434323-567391232.png
异常的分类

  • Error(错误,通常不可恢复)
    • VirtualMachineError:虚拟机错误
    • OutOfMemoryError:内存溢出
    • StackOverflowError:栈溢出
  • Exception(异常,可处理)
    • RuntimeException(运行时异常,非受检异常)
      • 编译时检查,必须处理
      • 继承自Exception(但不包括RuntimeException)
      • 示例:IOException、SQLException
    • 其他Exception(受检异常)
      • 编译时不强制检查
      • 包括RuntimeException及其子类,以及Error及其子类
      • 示例:NullPointerException、ArrayIndexOutOfBoundsException

异常的作:

  1. 异常是用来查询bug的关键参考信息。
  2. 异常可以作为方法内部的一种特殊返回值,以便通知调用者底层的执行情况。

二,异常的处理方式

2.1 JVM默认的处理方式

  • 把异常的名称,异常原因以及异常出现的位置等信息输出在控制台。
  • 程序停止执行,异常下面的代码不会再执行。

2.2 自己处理(捕获异常

捕获异常的目的:不让程序停止。

格式:

try{
    可能出现异常的代码;
} catch(异常类名 变量名){
    异常的处理代码;
}finally{
    //不管程序是否跳转到 catch 块中,finally 块中的代码都会被执行。
    //finally可以不写,但是一般情况下都建议编写,用来释放资源或者关闭流等操作
    System.out.println("finally 块中的代码已经执行完毕");
}

目的:当代码出现异常时,可以让程序继续往下执行。

举例:

try{
    //可能出现异常的代码
	System.out.println(2/0);//这里出现了异常,程序就会在这里创建一个ArithmeticException对象
    					//new ArithmeticException();
    					//拿着这个对象到catch的小括号中进行对比,看小括号中的变量能否接收这个对象
    					//如果能接收,就表示这个异常被捕获(抓住),执行catch里面对应的代码
    					//当catch里面所有的代码执行完毕,继续执行try...catch体系下面其他代码
}catch(ArithmeticException e){
    //如果出现了ArithmeticException异常,我该如何处理
	System.out.println("算数异常");
}finally{
    System.out.println("finally 块中的代码已经执行完毕");
}

使用情况:

  1. 如果 try 中没有遇到问题,那么就不会执行 catch 中的代码,而是执行 try 中代码。

  2. 如果 try 中遇到多个问题,会写多个 catch 与之对应

    如果要捕获多个异常,这些异常中如果存在继承关系,那么 ** 父类一定要写在最下面。** 这个注意下,很容易忽略!!

    在 jdk7 以后可以在 catch 中的同时捕获多个异常,中间用 |隔开

    catch(ArrayIndexOutOfBoundsException | ArithmeticException e )表示如果出现了 A 异常或 B 异常,采用同一种处理方案。

    public class Text {
        public static void main(String[] args){
            int[] arr={1,2,3,4,5,6};
            try{
                System.out.println(arr[10]);//如果没有写多个catch
                System.out.println(2/0);
                String s=null;
                System.out.println(s.equals("abc"));
            }catch (ArrayIndexOutOfBoundsException e){
                System.out.println("索引越界");
            }catch (ArithmeticException e){
                System.out.println("算数异常");
            }catch (NullPointerException e){
                System.out.println("空指针异常");
            }finally{
                System.out.println("finally 块中的代码已经执行完毕");
            }
            System.out.println("看看我执行了么");
        }
    }
    

    上面这么多 catch,只能输出第一个捕获到异常的 catch 中的代码。

    建议按照 try 中代码顺序依次写 catch 捕获对应的异常,增强可读性。

  3. 如果 try 中遇到的问题没有被捕获,即 catch 中写的依次与实际引发的异常不一样,这样就会无法捕获到异常。

    此时相当于 try catch 代码白写了,最终采用 JVM 进行默认处理。

  4. 如果 try 中遇到了异常,那么try 下面的其他代码就不会执行了

    public class Text {
        public static void main(String[] args){
            int[] arr={1,2,3,4,5,6};
            try{
                System.out.println(arr[10]);
                System.out.println("这里不执行了");//这段代码就不会执行
            }catch (ArrayIndexOutOfBoundsException e) {
                System.out.println("索引越界");
            }finally{
                System.out.println("finally 块中的代码已经执行完毕");
            }
            System.out.println("看看我执行了么");
        }
    }
    
    

异常中常用方法(Throwable 的成员方法):

方法名说明
public String getMessage()返回异常的简短描述
public String toString()返回异常的详细描述
public void printStackTrace()把异常的错误信息输出在控制台

第三个方法虽然会把异常的所有信息打印在控制台和 JVM 默认处理方法一样,但是!!不会停止运行下面的代码

第三个方法包含了上面两个方法的信息,是最常用的。

第三个方法在底层利用 System.err.println 进行输出,在控制太显示红色字体

2.3 try-catch-finally的执行顺序

//基本执行顺序
try {
    // 1. 先执行try块代码
    // 如果发生异常,跳转到catch
    // 如果有return,会先计算返回值,暂存
} catch {
    // 2. 如果有匹配的异常,执行catch块
    // 如果有return,会先计算返回值,暂存
} finally {
    // 3. 无论是否发生异常,都会执行finally
    // finally中的return会覆盖之前的返回值
}
// 4. 如果没有return,继续执行后续代码

总结

场景trycatchfinally返回值/异常
无异常,无return继续执行
无异常,try有returntry的return值
无异常,try和finally都有returnfinally的return值
有异常,catch处理catch的return值
有异常,catch和finally都有returnfinally的return值
finally中有异常可选finally的异常
try和catch都有异常catch的异常

重要规则

  1. finally总是执行:无论try/catch中是否有return或异常
  2. 返回值计算时机:return语句执行时计算返回值并暂存
  3. finally覆盖规则
    • finally中有return:覆盖try/catch的return
    • finally中有异常:覆盖try/catch的return和异常
    • finally中修改基本类型:不影响已暂存的返回值
    • finally中修改引用类型:可能影响返回的对象状态
  4. 异常链:如果try和catch都抛出异常,后抛出的异常会覆盖先前的

2.4 交给调用者处理(抛出异常

抛出异常的目的:告诉方法调用者这个方法出错了!

方法抛出异常要写两个部分:

  1. throws

    写在方法定义处,表示声明一个异常。告诉调用者,使用本方法可能有哪些异常。

    如果是编译时异常:必须要写

    如果是运行时异常:可以省略不写

    public void 方法() throws 异常类名1,异常类名2,....{
        
    }
    
  2. throw

    写在方法内,结束方法。手动抛出异常对象,交给调用者。方法中下面的代码不再执行了。

    public void 方法(){
        throw new NullPointerException();
        System.out.println("这部分代码不会执行");
    }
    
  3. 完整写法

    public void 方法() throws 异常类名1,异常类名2,...{//如果是runtime异常可以不写
        throw new NullPointerException();
        System.out.println("这部分代码不会执行");
    }
    

举例:

//写一个获取数组最大元素的方法
public int getMax(int[] arr){
    if(arr==null){
        //此时抛出异常
        throw new NullPointerException;
    }
    if(arr.length==0){
        throw new ArrayIndexOutOfBoundsException();
    }
    int max=arr[0];
    for(int i=1;i<arr.length;i++){
        if(arr[i]>max){
            max=arr[i]
        }
    }
    return max;
}
// 测试类
public class Text {
    public static void main(String[] args){
        int[] arr=null;
        int max=0
        try{
            max=getMax(arr);//将调用方法的代码用try进行捕捉,要不然下面的代码将无法执行。
        }catch(NullPointerException e){//空指针异常
            e.printStackTrace();
        }catch(ArrayIndexOutOfBoundsException e){//索引越界异常
            e.printStackTrace();
        }
    }
}

三,自定义异常

步骤:

  1. 定义异常类

  2. 写继承关系

    如果是运行时异常:继承RuntimeException

    如果是编译时异常:继承Exception,如果方法中 new 出编译时异常,我们需要在方法外部 throws 抛出。

  3. 空参构造

  4. 带参构造

自定义异常的意义:让控制台的报错信息更加见名知意。

可以在 Catch 中捕捉哪些 java 中没有定义的比如说名字规范等情况。

自定义异常的规范写法:

public class NameFormatException{}
//NameFormat:异常名字
//Exception:表示这是一个异常类
//使用大驼峰命名法

举例:

//自定义运行时异常
public class NameFormatException extends RuntimeException{//姓名格式异常(自定义的)(idea快捷键alt+insert自动补齐即可)
    public NameFormatException() {
    }
    public NameFormatException(String message) {//传入异常的信息
        super(message);
    }
}
//自定义编译时异常
public class NameFormatException extends Exception{
    public NameFormatException(String message) {
        super(message);
    }
    public NameFormatException() {

    }
}

import Exception.NameFormatException;
public class ExceptionExample {
    public static void main(String[] args) {
        try{
            saveName("");
        }catch(NameFormatException e){
            e.printStackTrace();
            System.out.println("Error saving name");
        }
    }
    
    public static void saveName(String name) throws NameFormatException{
        if(name.isEmpty()){
            throw new NameFormatException("名字不能为空");
        }else{
            System.out.println("Name saved");
        }
    }
    
}
动物装饰