一天一个知识点第四天

125 阅读7分钟

认识异常

异常分为两种{运行时异常RuntimeException}和{编译时异常}

运行时异常RuntimeException:运行时异常:继承自RuntimeException,编译阶段(写代码的时候)不会报错,运行时才会出现的异常。

编译时异常:写代码时就可能出现的错误提醒。

一、常见的运行时异常:(开发者水平不高时,经常出现)

1.ArrayIndexOutOfBoundsException 数组越界异常

int[] arr = {11,22,33};  
              0  1  2  
System.*out*.println(arr[0]);  
System.*out*.println(arr[1]);  
System.*out*.println(arr[2]);  
System.out.println(arr[3]);//ArrayIndexOutOfBoundsException 数组越界异常

2.ArithmeticException 数学计算异常

System.out.println(10/0);

3.NullPointerException 空指针异常 ( 企业开发中常见 )

String name = null;  
System.*out*.println(name);  
System.*out*.println(name.length());//NullPointerException 空指针异常

二、常见的编译时异常

1. 日期解析异常

//1.解析时间异常  
String dataStr = "2024-11-12 11:24:32";  
//2.创建一个简单的日期格式化对象  
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
//3.开始把字符串时间解析成日期对象  
Date d = sdf.parse(dataStr);

此处不确定dataStr是一个标准格式的时间,所以在转换时可能出错,所以parse会报错

这里补充快捷键:

查看前代(光标放到类上):ctrl+alt+U  
查看后代(光标放到类上):ctrl+H

image.png

image.png

image.png 解决方法有两种:

方式一:捕获异常 (try…catch)

这里有一个快捷键(ctrl+alt+T),选择第6个即可

private static void testException() {  
    try {  
        //1.解析时间异常  
        String dataStr = "2024-11-12 11:24:32";  
        //2.创建一个简单的日期格式化对象  
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");  
        //3.开始把字符串时间解析成日期对象  
        Date d = sdf.parse(dataStr);  
    } catch (Exception e) {  
        e.printStackTrace();  
    }  
}

方式二:抛出异常( throws

private static void testException() throws Exception {  
    //1.解析时间异常  
    String dataStr = "2024-11-12 11:24:32";  
    //2.创建一个简单的日期格式化对象  
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
    //3.开始把字符串时间解析成日期对象  
    Date d = sdf.parse(dataStr);  
  
    //读一个文件  
    FileInputStream is = new FileInputStream("D:/meinv.png");  
}

当代码中异常出现较多,我们直接抛出总异常 (Exception),这样throws这里就不会很复杂。

补:运行时异常的处理(推荐的处理方式)

若能预知错误信息

go  
//运行时异常:编译时不报错,运行的时候可能发生错误  
int[] arr = new int[10];  
int index = 11;  
//解决运行时异常推荐的解决方案:能加预知到的异常数据进行校验排除  
if (index < arr.length){  
    System.*out*.println(arr[11]);//编译时不报错,运行时发生异常  
}

三、异常的作用

为什么我们要处理异常呢?

答:我觉得非常重要的一点就是防止程序崩溃,捕获异常可以防止程序因未处理的错误而突然终止。

1、异常是用来定位程序bug的关键信息;

2、可以作为方法内部的一种特殊返回值,以便通知上层调用者,方法的执行问题。

其次还可以返回

java  
public static void main(String[] args) {  
    //目标:异常的作用  
    System.*out*.println(*device*(10,2));  
    System.*out*.println("----------------------------------");  
    System.*out*.println(*device*(10,0));  
}  
  
private static int device(int a,int b) {  
    if(b==0){  
        System.*out*.println("参数b不能为0");  
        throw new RuntimeException("/ 0");  
    }  
    return a / b;  
}

image.png

四、自定义异常

思考:为什么要自定义异常?

答:Java无法为这个世界上全部的问题都提供异常类来代表, 如果企业自己的某种问题,想通过异常来表示,以便用异常来管理该问题,那就需要自己来定义异常类了。

问题严重,用自定义编译异常(继承Exception);问题不严重,用自定义运行异常(继承RuntimeException);常用自定义运行异常(企业常用)。

步骤:

1、继承Exception
2、重写构造器:封装异常的错误信息。

3、抛出自定义编译时异常

自定义编译时异常:继承Exception

// 自定义编译时异常  
// 1、继承Exception  
// 2、重写构造器:封装异常的错误信息。  
public class AgeIllegalException extends Exception{  
    //重写无参构造器  
    public AgeIllegalException() {  
    }  
    //重写有参挂构造器  
    public AgeIllegalException(String message) {  
        super(message); *//* *调用父类有参构造器初始化异常消息*  
    }  
    *//* *后面使用异常会获取异常消息,使用父类的getMessage()方法获取异常消息*  
}

自定义运行时异常:继承RuntimeException

// 自定义运行时异常  
// 1、继承RuntimeException  
// 2、重写构造器:封装异常的错误信息。  
public class AgeIllegalRuntimeException extends RuntimeException{  
    public AgeIllegalRuntimeException() {  
    }  
  
    public AgeIllegalRuntimeException(String message) {  
        super(message); *//* *调用父类有参构造器初始化异常消息*  
    }  
}

下面是规范的处理方式:先抛出小的异常,为了系统的安全性,我们在抛出一个大的异常(当出现未知异常时) ---> 推荐,更符合企业规范!!!

typescript  
public static void main(String[] args) {  
    //目标:设置有效的年龄,有效年龄范围0~200,当年龄无效抛出年龄不合法的自定义异常  
    try {  
        *setAge*(100);  
        *setAge*(203);  
    } catch (AgeIllegalRuntimeException e) {  
        e.printStackTrace();//打印异常堆栈信息到控制台(异常完整信息,以后会写入日志文件),给开发人员看的  
        System.*out*.println("发生错误:"+e.getMessage()+",请重新输入年龄");//给用户看的(后面,这个信息会给到前端)  
    } catch (Exception e) {  
        e.printStackTrace();//打印异常堆栈信息到控制台(异常完整信息,以后会写入日志文件),给开发人员看的  
        System.*out*.println("系统繁忙,请稍后再试");//给用户看的(后面,这个信息会给到前端)  
    }  
}  
  
public static void setAge(int age){  
    if (age < 0 || age > 200){  
        //throw new RuntimeException("年龄不合法");//使用父类异常导致职责不清晰  
  
        //以后项目中异常要分类:  
        //  年龄异常,不仅给用户友好的消息提示,还要给管理员发送短信通知年龄和合法错误  
        //  运行时异常异常,不仅给用户友好的消息提示,还要给管理员发送短信未知错误  
        //如何解决:自定义年龄不合法异常,可以异常类型职责清晰  
  
        //3.抛出自定义运行时异常  
        //throw new 异常类型("友好的异常消息")  
        throw new AgeIllegalRuntimeException("年龄不合法");  
    }  
    System.*out*.println(age+"年龄合法,保存成功");  
}

问:企业开发中为什么常用自定义运行时异常,很少使用自定义编译时异常?

答:减少代码侵入性(避免强制 try-catch 或 throws 声明)、符合 Spring 等框架的设计(如事务默认对运行时异常回滚)、避免过度捕获逻辑(由全局异常处理器统一处理),以及保持业务代码简洁性(仅对关键错误显式处理,其余交由上层统一管控)。这种策略使代码更灵活,降低耦合,更适合企业级分层架构和微服务场景。

年龄异常,不仅给用户友好的消息提示,还要给管理员发送短信通知年龄和合法错误
运行时异常异常,不仅给用户友好的消息提示,还要给管理员发送短信未知错误

五、异常处理方案

image.png

开发过程中调用A方法时-->A调用B-->B调用C;C出现异常,C抛给B-->B抛给A-->A捕获处理异常

方案一:底层异常层层往上抛出,最外层捕获异常,记录下异常信息,并响应适合用户观看的信息进行提示.

例子:比如在京东官网。京东:re.jd.com/ ----》re.jd.com/dasd/dad/sd…

被最外层捕获异常,记录下异常信息,并响应适合用户观看的信息进行提示:www.jd.com/error2.aspx

image.png 方案二:最外层捕获异常后,尝试重新修复

public static void main(String[] args) {  
    // 目标:捕获异常后,尝试重新修复。  
    while (true) {  
        try {  
            *getPrice*();  
            break;  
        } catch (Exception e) {  
            System.*out*.println("价格输入有误,请重新输入!");  
        }  
    }  
}  
  
public static double getPrice(){  
    Scanner sc = new Scanner(System.*in*);  
    System.*out*.println("请输入商品价格:");  
    double price = sc.nextDouble();  
    return price;  
}