一天一个Java知识点----异常

78 阅读6分钟

异常

认识异常

异常分为两种{运行时异常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

解决方法有两种:

方式一:捕获异常(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这里就不会很复杂。

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

若能预知错误信息

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

异常的作用

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

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

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

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

其次还可以返回

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;
}

自定义异常

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

答: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); //调用父类有参构造器初始化异常消息
    }
}

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

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 等框架的设计(如事务默认对运行时异常回滚)、避免过度捕获逻辑(由全局异常处理器统一处理),以及保持业务代码简洁性(仅对关键错误显式处理,其余交由上层统一管控)。这种策略使代码更灵活,降低耦合,更适合企业级分层架构和微服务场景。

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

异常处理方案

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

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

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

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

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;
}