Java异常_18
异常
异常(Exception)和错误(Error)都是Throwable类的子类,但它们有不同的用途和继承关系
Throwable
├── Error
│ ├── OutOfMemoryError
│ ├── StackOverflowError
│ └── ...
└── Exception
├── 运行时异常(RuntimeException)
│ ├── NullPointerException
│ ├── ArrayIndexOutOfBoundsException
│ └── ...
└── 编译时异常()
├── IOException
├── SQLException
└── ...
错误
- 表示程序无法处理的严重系统级问题,通常由 JVM 或底层资源故障引发,如内存溢出(OutOfMemoryError)、栈溢出(StackOverflowError)等
- 属于 Throwable 类的子类,但无法通过代码捕获或处理
- 通常由 JVM 抛出,程序无法主动恢复,会导致应用终止
异常
- 异常(Exception)是Java中用于处理程序运行时错误的一种机制。异常处理使得程序在遇到错误时能够优雅地恢复或终止,而不是直接崩溃。Java的异常处理机制基于抛出(throw)和捕获(catch)异常
- 程序运行时可预见或可恢复的非致命问题,如文件找不到(FileNotFoundException)、空指针(NullPointerException)等
- 继承自 Throwable,但属于应用程序级别的错误,可通过 try-catch 处理
- 分为 编译时异常(Checked Exception) 和 运行时异常(RuntimeException)
编译时异常
-
编译时异常是Java中一种在编译时强制要求处理的异常。编译器会检查这些异常是否被正确处理(捕获或声明抛出),如果没有处理,代码将无法通过编译
-
常见的编译时异常
-
IOException,表示输入输出操作中的异常,通常与文件读写、网络通信等相关
-
SQLException,表示数据库操作中的异常,通常与JDBC操作相关
-
ClassNotFoundException,表示类未找到异常,通常与类加载相关
public class Main { public static void main(String[] args) { try { Class.forName("com.example.NonExistentClass"); } catch (ClassNotFoundException e) { System.out.println("Class not found: " + e.getMessage()); } } }
-
-
编译时异常的处理方式
编译时异常必须被处理,否则代码将无法通过编译。处理编译时异常的方式有两种
-
捕获异常:使用try-catch语句捕获并处理异常
import java.io.FileInputStream; import java.io.FileNotFoundException; public class Main { public static void main(String[] args) { try { FileInputStream file = new FileInputStream("nonexistent.txt"); } catch (FileNotFoundException e) { System.out.println("File not found: " + e.getMessage()); } } } -
声明抛出异常:使用throws关键字声明方法可能抛出的异常,将异常交给调用者处理
import java.io.FileInputStream; import java.io.FileNotFoundException; public class Main { public static void main(String[] args) throws FileNotFoundException { FileInputStream file = new FileInputStream("nonexistent.txt"); } }
-
运行时异常
-
运行时异常是Java中一种在运行时才会被检测到的异常。与编译时异常不同,运行时异常不需要在代码中显式捕获或声明抛出,编译器不会强制要求处理这些异常
-
运行时异常通常表示程序逻辑错误(如空指针、数组越界等),而不是外部资源或环境问题
-
常见运行时异常
-
NullPointerException,表示尝试访问空对象的成员(如方法或字段)
-
ArrayIndexOutOfBoundsException,表示数组访问越界
-
ArithmeticException,表示算术运算错误(如除零)
-
IllegalArgumentException,表示传递给方法的参数不合法
public class Main { public static void main(String[] args) { setAge(-1); // 抛出 IllegalArgumentException } public static void setAge(int age) { if (age < 0) { throw new IllegalArgumentException("Age cannot be negative"); } } } -
ClassCastException,表示类型转换错误
-
-
运行时异常的处理方式
运行时异常不需要在代码中显式捕获或声明抛出,但可以通过以下方式处理
- 捕获异常:使用try-catch语句捕获并处理异常
- 避免异常:通过代码逻辑避免异常的发生
自定义异常
-
自定义编译时异常
自定义编译时异常需要继承 Exception 类。编译器会强制要求处理这些异常
-
自定义运行时异常
运行时异常需要继承 RuntimeException 类。编译器不会强制要求处理这些异常
一般定义为运行时异常
// 自定义运行时异常 class MyUncheckedException extends RuntimeException { // 无参构造函数 public MyUncheckedException() { super(); } // 带消息的构造函数 public MyUncheckedException(String message) { super(message); } // 带消息和原因的构造函数 public MyUncheckedException(String message, Throwable cause) { super(message, cause); } // 带原因的构造函数 public MyUncheckedException(Throwable cause) { super(cause); } }public class Main { public static void main(String[] args) { validateAge(-1); // 调用可能抛出异常的方法 } // 方法抛出自定义运行时异常 public static void validateAge(int age) { if (age < 0) { throw new MyUncheckedException("Age cannot be negative"); } } }
异常处理方式
-
try-catch-finally 语句
-
try-catch-finally 是Java中最常用的异常处理机制
try { // 可能抛出异常的代码 } catch (ExceptionType e) { // 捕获并处理异常 } finally { // 无论是否发生异常,都会执行的代码 } -
避免空的 catch 块,空的catch块会隐藏错误,导致调试困难。即使不需要处理异常,至少应该记录日志或输出错误信息
-
try 块必须至少有一个 catch 块或一个 finally 块,否则会编译错误
-
无论是否发生异常,finally块中的代码都会执行。通常用于释放资源(如关闭文件、数据库连接等)
-
catch语句可以有多个
-
多个catch语句的顺序非常重要,必须从具体到通用。也就是说,子类异常(更具体的异常)应该放在父类异常(更通用的异常)之前
try { // 可能抛出异常的代码 } catch (FileNotFoundException e) { System.out.println("File not found: " + e.getMessage()); } catch (IOException e) { System.out.println("IO error: " + e.getMessage()); } catch (Exception e) { System.out.println("Caught exception: " + e.getMessage()); } -
从Java 7开始,可以在一个catch块中捕获多个异常类型,用|分隔。这样可以避免重复代码
try { // 可能抛出异常的代码 } catch (FileNotFoundException | IOException e) { System.out.println("File or IO error: " + e.getMessage()); } catch (Exception e) { System.out.println("General exception: " + e.getMessage()); } -
如果如果抛出的异常类型与catch块声明的异常类型不匹配,catch块无法捕获异常,异常会向上传递,最终由JVM处理,程序终止并打印异常堆栈信息
-
由于上一条原因所以会有try-finally语句,不管有没有成功运行都要执行finally语句块中的内容
try-with-resources 的结构
-
try-with-resources 是Java 7引入的一种语法结构,用于自动管理资源(如文件、数据库连接、网络连接等)。它的主要目的是简化资源管理代码,确保资源在使用完毕后能够被正确关闭,避免资源泄漏
-
try-with-resources 语句会自动调用资源的 close() 方法,无论 try 块中的代码是否抛出异常。这比传统的 try-catch-finally 结构更加简洁和安全
-
基本结构如下
try (资源声明) { // 使用资源的代码 } catch (异常类型 e) { // 处理异常 } finally { // 可选的 finally 块 }- 资源声明:在 try 后面的括号中声明并初始化资源。可以声明多个资源,用分号分隔
- 资源类型:资源必须实现 AutoCloseable 或 Closeable 接口
- 自动关闭:无论 try 块中的代码是否抛出异常,资源都会在 try 块结束时自动关闭
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class Main { public static void main(String[] args) { try ( FileInputStream input = new FileInputStream("input.txt"); FileOutputStream output = new FileOutputStream("output.txt") ) { int data; while ((data = input.read()) != -1) { output.write(data); } } catch (IOException e) { System.out.println("Caught exception: " + e.getMessage()); } } }
-
-
throw 关键字
-
throw 关键字用于手动抛出异常。通常用于在满足某些条件时抛出异常
public class Main { public static void main(String[] args) { try { throw new RuntimeException("Manual exception"); } catch (RuntimeException e) { System.out.println("Caught exception: " + e.getMessage()); } } }
-
-
throws 关键字
-
声明方法可能抛出的异常:告诉调用者,该方法可能会抛出某些异常,调用者需要处理这些异常(捕获或继续抛出)
-
throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父亲类型
-
如果抛出的是父亲类型,那么在调用该方法时catch也要是父类类型或者更上面的类型
返回类型 方法名(参数列表) throws 异常类型1, 异常类型2, ... { // 方法体 } public class FileReader { // 在定义方法时声明可能抛出的异常 public static void readFile(String filename) throws FileNotFoundException { FileInputStream file = new FileInputStream(filename); System.out.println("File opened successfully"); } } -
将异常传递给调用者:如果方法内部发生了异常,但没有捕获和处理,可以使用throws将异常抛给调用者
public class Main { public static void main(String[] args) throws FileNotFoundException { FileReader.readFile("nonexistent.txt"); // 调用可能抛出异常的方法 } }
异常抑制
-
在传统的 try-catch-finally 结构中,如果在 try 块中抛出异常,同时在 finally 块中也抛出异常,那么 finally 块中的异常会覆盖 try 块中的异常,导致 try 块中的异常信息丢失
-
在下面的例子中,最终只会抛出 finally 块中的异常,try 块中的异常信息会丢失
public class Main { public static void main(String[] args) { try { throw new RuntimeException("Exception in try block"); } finally { throw new RuntimeException("Exception in finally block"); } } } Exception in thread "main" java.lang.RuntimeException: Exception in finally block at Main.main(Main.java:6)
try-with-resources 的异常抑制机制
- 如果 try 块中抛出异常,同时资源的 close() 方法也抛出异常,那么 try 块中的异常会被正常抛出,而 close() 方法抛出的异常会被抑制
- 被抑制的异常可以通过 Throwable.getSuppressed() 方法获取
例如:
-
以下是一个自定义资源类,它在 close() 方法中抛出异常
class MyResource implements AutoCloseable { @Override public void close() throws Exception { throw new Exception("Exception in close()"); } } -
使用 try-with-resources
在 try-with-resources 语句中使用这个资源,并在 try 块中抛出异常
public class Main { public static void main(String[] args) { try (MyResource resource = new MyResource()) { throw new RuntimeException("Exception in try block"); } catch (Exception e) { System.out.println("Caught exception: " + e.getMessage()); for (Throwable suppressed : e.getSuppressed()) { System.out.println("Suppressed exception: " + suppressed.getMessage()); } } } } Caught exception: Exception in try block Suppressed exception: Exception in close()- try 块中抛出了 RuntimeException("Exception in try block")。
- close() 方法抛出了 Exception("Exception in close()")
- try 块中的异常会被正常抛出,而 close() 方法抛出的异常会被抑制
-
Throwable.getSuppressed() 方法
-
Throwable.getSuppressed() 方法返回一个数组,包含所有被抑制的异常。这些异常通常是在 try-with-resources 语句中,资源关闭时抛出的异常
for (Throwable suppressed : e.getSuppressed()) { System.out.println("Suppressed exception: " + suppressed.getMessage()); }
-
-

浙公网安备 33010602011771号