目录
异常的概念与体系结构
异常的概念:
在程序中不正常行为称为异常,如算术异常、数组越界异常、空指针异常等。
例如:
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) { // 防御式编程示例 - LBYL String str = null; if (str != null) { System.out.println(str.length()); // 不会执行,避免空指针异常 } // 防御式编程示例 - EAFP int[] 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块捕获并处理这些自定义异常,确保程序在异常情况下能够正常运行。