目录
异常的概念与体系结构
异常的概念:
异常的体系结构:
异常的处理方式
防御式编程:
异常的抛出:
异常的捕获:
finally:
代码示例:
异常的处理流程
自定义异常类
举例:
异常的概念与体系结构
异常的概念:
在程序中不正常行为称为异常,如算术异常、数组越界异常、空指针异常等。
例如:
public class ExceptionDemo {public static void main(String[] args) {// 示例算术异常try {int result = 10 / 0; // 除以0会抛出算术异常} catch (ArithmeticException e) {System.out.println("算术异常:" + e.getMessage());}// 示例数组越界异常try {int[] arr = {1, 2, 3};System.out.println(arr[100]); // 访问数组越界会抛出数组越界异常} catch (ArrayIndexOutOfBoundsException e) {System.out.println("数组越界异常:" + e.getMessage());}// 示例空指针异常try {String str = null;System.out.println(str.length()); // 对空指针调用方法会抛出空指针异常} catch (NullPointerException e) {System.out.println("空指针异常:" + e.getMessage());}} }
异常的体系结构:
Throwable是异常体系的顶层类,派生出Error和Exception两个重要的子类,分为编译时异常和运行时异常。
异常的处理方式
防御式编程:
LBYL(Look Before You Leap):在操作之前进行充分的检查,事前防御型。例如,在代码中先检查条件,再执行操作,以避免异常情况的发生。
EAFP(It's Easier to Ask Forgiveness than Permission):先操作,遇到问题再处理,事后认错型。这种方式更关注正常流程,代码更清晰,容易理解。
异常的抛出:
使用throw关键字抛出异常对象,将错误信息告知给调用者。抛出的异常对象必须是Exception或其子类对象,根据异常类型不同,处理方式也不同。
异常的捕获:
通过try-catch捕获并处理异常,可以在catch块中对捕获到的异常进行具体处理。如果异常无法在当前方法中处理,可以继续向上抛出或交给调用者处理。
异常声明throws:在方法声明时使用throws关键字将异常抛给方法的调用者处理,提醒调用者处理异常。
finally:
finally块用于资源清理的扫尾工作,无论是否发生异常,finally中的代码都会被执行。一般用于释放资源,确保资源得到正确释放。
代码示例:
public class ExceptionHandlingDemo {public static void main(String[] args) {// 防御式编程示例 - LBYLString str = null;if (str != null) {System.out.println(str.length()); // 不会执行,避免空指针异常}// 防御式编程示例 - EAFPint[] arr = {1, 2, 3};try {System.out.println(arr[100]); // 尝试访问数组越界} catch (ArrayIndexOutOfBoundsException e) {System.out.println("数组越界异常:" + e.getMessage());}// 异常的抛出示例try {validateAge(-5); // 抛出自定义异常} catch (IllegalArgumentException e) {System.out.println("年龄不能为负数:" + e.getMessage());}// 异常的捕获示例try {int result = divide(10, 0); // 抛出算术异常} catch (ArithmeticException e) {System.out.println("算术异常:" + e.getMessage());}// finally示例try {System.out.println("Try block");} finally {System.out.println("Finally block - resource cleanup");}}// 自定义异常类static void validateAge(int age) {if (age < 0) {throw new IllegalArgumentException("年龄不能为负数");}}// 抛出算术异常static int divide(int a, int b) {if (b == 0) {throw new ArithmeticException("除数不能为0");}return a / b;} }
在上述代码中,演示了防御式编程中的LBYL和EAFP方式,异常的抛出和捕获,以及finally块的使用。通过这些示例,展示了不同的异常处理方式在实际代码中的应用。
异常的处理流程
- 程序首先执行try中的代码块。
- 如果try中的代码块出现异常,程序会立即跳转到对应的catch块进行异常处理。
- 如果找到匹配的异常类型,就会执行对应的catch块中的代码。
- 如果没有找到匹配的异常类型,异常会被向上抛出,继续传递给上层调用者。
- 无论是否找到匹配的异常类型,finally块中的代码都会被执行,用于进行资源清理等操作。
- 如果异常一直向上传递到main方法仍未被处理,最终会交给JVM处理,导致程序异常终止。
下面是一个示例代码,演示了完整的异常处理流程:
public class ExceptionHandlingFlow { public static void main(String[] args) {try {int result = divide(10, 0); // 抛出算术异常} catch (ArithmeticException e) {System.out.println("算术异常:" + e.getMessage());// 继续向上抛出异常throw e;} finally {System.out.println("finally块中的代码一定会执行");}}// 抛出算术异常static int divide(int a, int b) {try {if (b == 0) {throw new ArithmeticException("除数不能为0");}return a / b;} catch (ArithmeticException e) {System.out.println("捕获到算术异常:" + e.getMessage());// 继续向上抛出异常throw e;} finally {System.out.println("divide方法的finally块中的代码一定会执行");}} }
运行结果:
在上述代码中,通过try-catch-finally块展示了完整的异常处理流程。无论是否出现异常,finally块中的代码都会被执行,用于进行必要的资源清理操作。这样可以确保程序在异常情况下也能够正常运行并保持稳定性。
自定义异常类
- 自定义异常类继承自Exception或RuntimeException,用于表示实际开发中遇到的特定异常。
- 可以实现带有String类型参数的构造方法,用于说明异常的原因。
- 自定义异常通常继承自Exception或RuntimeException,分为受查异常和非受查异常。
举例:
// 自定义用户名异常类,继承自Exception,表示用户名错误 class UserNameException extends Exception {public UserNameException(String message) {super(message);} }// 自定义密码异常类,继承自Exception,表示密码错误 class PasswordException extends Exception {public PasswordException(String message) {super(message);} }public class CustomExceptionExample {// 模拟用户登录,抛出自定义异常public static void login(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 {login("admin", "12345"); // 模拟登录,抛出异常} catch (UserNameException e) {System.out.println("用户名异常:" + e.getMessage());// 可以在此处处理用户名异常} catch (PasswordException e) {System.out.println("密码异常:" + e.getMessage());// 可以在此处处理密码异常}} }
在上述代码中,我们定义了两个自定义异常类
UserNameException
和PasswordException
,分别用于表示用户名错误和密码错误的异常。这些异常类继承自Exception
,表示受查异常,需要在方法声明中进行处理或抛出。在login
方法中模拟用户登录过程,如果用户名或密码错误,则抛出相应的自定义异常。在main
方法中通过try-catch块捕获并处理这些自定义异常,确保程序在异常情况下能够正常运行。