Java从入门到“放弃”(精通)之旅——JavaSE终篇(异常)

Java从入门到"放弃"(精通)之旅🚀------JavaSE终篇(异常)

一、异常的概念与体系结构

1.1 什么是异常?

在生活中,当一个人表情痛苦时,我们可能会关心地问:"你是不是生病了?"在程序中也是如此,即使我们绞尽脑汁将代码写得尽善尽美,程序运行时仍可能出现各种问题,比如数据格式不对、网络不通畅、内存不足等。

在Java中,将程序执行过程中发生的不正常行为称为异常。例如:

java 复制代码
// 算术异常
System.out.println(10 / 0);  // ArithmeticException: / by zero

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

// 空指针异常
int[] arr = null;
System.out.println(arr.length);  // NullPointerException

1.2 异常的体系结构

Java中的异常种类繁多,为了分类管理,Java维护了一个异常体系结构:

复制代码
Throwable
├── Error
└── Exception
    ├── IOException
    ├── ClassNotFoundException
    ├── CloneNotSupportedException
    └── RuntimeException
        ├── ArithmeticException
        ├── NullPointerException
        ├── ArrayIndexOutOfBoundsException
        └── ...
  • Throwable:异常体系的顶层类
  • Error:Java虚拟机无法解决的严重问题,如StackOverflowError和OutOfMemoryError
  • Exception:程序员可以通过代码处理的异常

1.3 异常的分类

根据发生时机,异常可分为:

  1. 编译时异常(Checked Exception):在编译期间就能发现的异常
  2. 运行时异常(Unchecked Exception):RuntimeException及其子类,在程序运行时才会出现
java 复制代码
// 编译时异常示例
@Override
public Person clone() {
    return (Person)super.clone();  // 需要处理CloneNotSupportedException
}

// 运行时异常示例
int[] arr = null;
System.out.println(arr.length);  // NullPointerException

二、异常的处理方式

2.1 防御式编程的两种风格

  1. LBYL(Look Before You Leap):事前检查
java 复制代码
boolean ret = false;
ret = 登陆游戏();
if (!ret) {
    处理登陆游戏错误;
    return;
}
ret = 开始匹配();
if (!ret) {
    处理匹配错误;
    return;
}
// ...更多检查
  1. EAFP(It's Easier to Ask Forgiveness than Permission):事后处理
java 复制代码
try {
    登陆游戏();
    开始匹配();
    游戏确认();
    选择英雄();
    载入游戏画面();
} catch (登陆游戏异常) {
    处理登陆游戏异常;
} catch (开始匹配异常) {
    处理开始匹配异常;
}
// ...更多catch块

Java主要采用EAFP风格,通过五个关键字处理异常:throwtrycatchfinallythrows

2.2 异常的抛出(throw)

使用throw关键字抛出异常:

java 复制代码
public static int getElement(int[] array, int index) {
    if (null == array) {
        throw new NullPointerException("传递的数据为null");
    }
    if (index < 0 || index >= array.length) {
        throw new ArrayIndexOutOfBoundsException("传递的数组下标越界");
    }
    return array[index];
}

public static void main(String[] args) {
    int[] array = {1, 2, 3};
    getElement(array, 3);  // 抛出ArrayIndexOutOfBoundsException
}

2.3 异常的捕获

2.3.1 异常声明(throws)
java 复制代码
public class Config {
    public void openConfig(String filename) throws FileNotFoundException {
        if (!filename.equals("config.ini")) {
            throw new FileNotFoundException("配置文件名字不对");
        }
        // 打开文件
    }
}

public static void main(String[] args) throws FileNotFoundException {
    Config config = new Config();
    config.openConfig("config.ini");
}
2.3.2 try-catch捕获处理
java 复制代码
public static void main(String[] args) {
    Config config = new Config();
    try {
        config.openConfig("config.txt");
        System.out.println("文件打开成功");
    } catch (FileNotFoundException e) {
        System.out.println("文件不存在,错误信息:" + e.getMessage());
        e.printStackTrace();  // 打印完整调用栈
    }
    System.out.println("异常处理后的代码");
}
2.3.3 finally块
java 复制代码
public static int getData() {
    Scanner sc = null;
    try {
        sc = new Scanner(System.in);
        int data = sc.nextInt();
        return data;
    } catch (InputMismatchException e) {
        e.printStackTrace();
    } finally {
        System.out.println("finally中代码");
        if (null != sc) {
            sc.close();  // 确保资源被释放
        }
    }
    return 0;
}

面试题 :finally中的语句一定会执行吗?

答:正常情况下一定会执行,除非遇到:

  1. 在try或catch块中调用System.exit()
  2. 程序所在线程死亡
  3. 关闭CPU

三、异常处理流程

java 复制代码
public static void main(String[] args) {
    try {
        func();
    } catch (ArrayIndexOutOfBoundsException e) {
        e.printStackTrace();
    }
    System.out.println("after try catch");
}

public static void func() {
    int[] arr = {1, 2, 3};
    System.out.println(arr[100]);  // 抛出异常
}

异常处理流程总结:

  1. 执行try中的代码
  2. 出现异常则匹配catch块
  3. 找到匹配则执行catch,否则向上传递
  4. 无论是否匹配,finally都会执行
  5. 如果一直未被处理,最终由JVM处理,程序终止

四、自定义异常类

Java内置异常类不能满足所有需求时,可以自定义异常:

java 复制代码
// 自定义用户名异常
class UserNameException extends Exception {
    public UserNameException(String message) {
        super(message);
    }
}

// 自定义密码异常
class PasswordException extends Exception {
    public PasswordException(String message) {
        super(message);
    }
}

// 使用自定义异常
public class Login {
    private String userName = "admin";
    private String password = "123456";

    public static void loginInfo(String userName, String password) 
            throws UserNameException, PasswordException {
        if (!userName.equals("admin")) {
            throw new UserNameException("用户名错误!");
        }
        if (!password.equals("123456")) {
            throw new PasswordException("密码错误!");
        }
        System.out.println("登陆成功");
    }

    public static void main(String[] args) {
        try {
            loginInfo("admin", "123");
        } catch (UserNameException e) {
            System.out.println("用户名异常:" + e.getMessage());
        } catch (PasswordException e) {
            System.out.println("密码异常:" + e.getMessage());
        }
    }
}

注意事项:

  • 自定义异常通常会继承自 Exception 或者 RuntimeException
  • 继承自 Exception 的异常默认是受查异常
  • 继承自 RuntimeException 的异常默认是非受查异常.

总结

Java异常处理是编写健壮程序的关键技术。通过合理的异常处理,我们可以:

  1. 提高程序的容错性
  2. 分离正常逻辑和错误处理逻辑
  3. 提供更清晰的错误信息
  4. 确保资源被正确释放

记住异常处理的原则:对可恢复情况使用受检异常,对编程错误使用运行时异常,避免滥用异常处理机制。



JavaSE往期专栏

相关推荐
echo1754255 分钟前
Apipost免费版、企业版和私有化部署详解
java
夜夜敲码11 分钟前
C语言教程(十六): C 语言字符串详解
c语言·开发语言
宋康18 分钟前
C语言结构体和union内存对齐
c语言·开发语言
异常君23 分钟前
Java 高并发编程:等值判断的隐患与如何精确控制线程状态
java·后端·代码规范
异常君23 分钟前
Java 日期处理:SimpleDateFormat 线程安全问题及解决方案
java·后端·代码规范
都叫我大帅哥25 分钟前
Spring AI中的ChatClient:从入门到精通,一篇搞定!
java·spring·ai编程
都叫我大帅哥26 分钟前
《@SpringBootApplication:Spring Boot的"一键启动"按钮,还是程序员的"免死金牌"?》
java·后端·spring
居然是阿宋28 分钟前
Kotlin高阶函数 vs Lambda表达式:关键区别与协作关系
android·开发语言·kotlin
triticale30 分钟前
P12167 [蓝桥杯 2025 省 C/Python A] 倒水
java·蓝桥杯
-曾牛1 小时前
Spring AI 快速入门:从环境搭建到核心组件集成
java·人工智能·spring·ai·大模型·spring ai·开发环境搭建