目录
- 什么是异常?
- 为什么需要
try-catch? try-catch的基本语法try-catch-finally结构try-catch-catch(多重catch)try-with-resources(Java 7+)throws关键字与try-catch的关系- 最佳实践和注意事项
什么是异常?
在 Java 中,异常是程序在运行时出现的不正常事件,它会中断正常的指令流程,Java 使用面向对象的方式来处理异常,所有异常类都是 java.lang.Throwable 类的子类。

Throwable 有两个主要的子类:
Error(错误):由 JVM(Java 虚拟机)系统内部错误或资源耗尽引起。OutOfMemoryError、StackOverflowError,这类错误通常是致命的,我们无法在代码中处理它,只能尽量避免。Exception(异常):由程序逻辑、环境问题等引起的问题,是我们可以在代码中进行捕获和处理的,它又分为两类:- 受检异常:编译器会强制要求你处理(要么用
try-catch捕获,要么用throws声明抛出)。IOException(文件读写错误)、SQLException(数据库操作错误)。 - 非受检异常:编译器不强制要求处理,通常是由程序逻辑错误引起的。
NullPointerException(空指针异常)、ArrayIndexOutOfBoundsException(数组越界异常)、ArithmeticException(算术异常,如除以零)。
- 受检异常:编译器会强制要求你处理(要么用
我们通常所说的 try-catch 处理的是 Exception 及其子类。
为什么需要 try-catch?
没有异常处理机制,当程序发生错误时,会立即终止,并向用户显示一串难懂的堆栈跟踪信息,用户体验极差,使用 try-catch 的好处:
- 增强程序的健壮性:程序不会因为一个意外错误而崩溃,可以优雅地处理错误。
- 提供友好的错误提示:可以向用户展示易于理解的错误信息,而不是一堆技术术语。
- 便于调试和维护:通过捕获异常,可以记录错误日志,帮助开发者定位问题。
- 分离业务逻辑和错误处理:将可能出错的代码(如文件操作、网络请求)和错误处理逻辑分离开,使代码更清晰。
try-catch 的基本语法
这是最简单的用法,用于捕获一种异常。

try {
// 可能会抛出异常的代码块
// JVM 在这里监视代码的执行
} catch (ExceptionType1 e) {
// 当 try 块中发生 ExceptionType1 类型的异常时,执行这里的代码
// e 是异常对象,包含了错误信息、堆栈跟踪等
}
工作原理:
- JVM 执行
try块中的代码。 try块中的代码没有发生任何异常,catch块将被忽略,程序继续执行try-catch结构之后的代码。try块中的代码发生了异常,JVM 会立即中断try块的执行,并寻找与该异常类型匹配的catch块。- 如果找到了匹配的
catch块,程序就跳转到该catch块中执行。 - 执行完
catch块后,程序继续执行try-catch结构之后的代码。
示例:
public class TryCatchExample {
public static void main(String[] args) {
int a = 10;
int b = 0;
try {
// 这行代码会抛出 ArithmeticException
int result = a / b;
System.out.println("计算结果是: " + result); // 这行代码不会执行
} catch (ArithmeticException e) {
// 捕获除数为零的异常
System.out.println("错误:不能除以零!");
// 打印详细的错误堆栈信息(非常有助于调试)
e.printStackTrace();
}
System.out.println("程序继续执行...");
}
}
输出:
错误:不能除以零!
java.lang.ArithmeticException: / by zero
at TryCatchExample.main(TryCatchExample.java:8)
程序继续执行...
try-catch-finally 结构
finally 块是可选的,但它包含的代码无论是否发生异常,也无论异常是否被捕获,都会被执行。

finally 块用于执行一些“清理”工作,比如关闭文件、释放数据库连接、释放锁等。
语法:
try {
// 可能会抛出异常的代码块
} catch (ExceptionType e) {
// 异常处理代码
} finally {
// 无论是否发生异常,这里的代码都会被执行
}
示例:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FinallyExample {
public static void main(String[] args) {
FileInputStream fis = null;
try {
fis = new FileInputStream("non_existent_file.txt");
// 读取文件...
} catch (FileNotFoundException e) {
System.out.println("文件未找到,请检查路径。");
} finally {
// 确保文件流被关闭,防止资源泄露
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
System.out.println("关闭文件流时出错。");
}
}
System.out.println("finally 块执行完毕,资源已清理。");
}
System.out.println("程序结束。");
}
}
输出:
文件未找到,请检查路径。
finally 块执行完毕,资源已清理。
程序结束。
finally 块的特殊情况:
如果在 try 或 catch 块中执行了 System.exit() 退出 JVM,或者 JVM 崩溃,finally 块不会被执行。
try-catch-catch(多重 catch)
一个 try 块后面可以跟多个 catch 块,用于处理不同类型的异常。
重要规则: 当多个 catch 块存在时,异常处理的顺序是从上到下的,子类异常必须放在父类异常的前面,否则会导致编译错误。
语法:
try {
// 可能会抛出多种异常的代码
} catch (SubExceptionType e) {
// 处理子类异常
} catch (ParentExceptionType e) {
// 处理父类异常
}
示例:
public class MultiCatchExample {
public static void main(String[] args) {
String str = "Hello";
int index = 10;
try {
// 这两行代码可能抛出两种不同的异常
char ch = str.charAt(index);
int num = Integer.parseInt(str);
} catch (StringIndexOutOfBoundsException e) {
// 捕获字符串越界异常
System.out.println("字符串索引越界了!");
} catch (NumberFormatException e) {
// 捕获数字格式异常
System.out.println("无法将字符串转换为数字!");
} catch (Exception e) {
// 捕获其他所有未预料到的异常(作为最后的保障)
System.out.println("发生了一个未知错误: " + e.getMessage());
}
System.out.println("程序继续执行...");
}
}
输出:
字符串索引越界了!
程序继续执行...
try-with-resources (Java 7+)
这是 Java 7 引入的一个简化资源管理的语法,它确保了在 try 语句中声明的资源(实现了 AutoCloseable 接口)在语句结束时被自动关闭,无需在 finally 块中手动关闭。
语法:
try (ResourceType resource1 = new ResourceType(); ResourceType resource2 = new ResourceType()) {
// 使用资源的代码
} catch (ExceptionType e) {
// 异常处理
}
// 资源在这里会自动关闭
示例:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class TryWithResourcesExample {
public static void main(String[] args) {
// BufferedReader 会在 try 块结束时自动关闭
try (BufferedReader reader = new BufferedReader(new FileReader("myfile.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.out.println("读取文件时发生错误: " + e.getMessage());
}
// 不需要在这里写 reader.close(),JVM 会自动处理
System.out.println("文件已自动关闭。");
}
}
throws 关键字与 try-catch 的关系
throws 关键字用在方法签名上,用于声明该方法可能会抛出哪些受检异常,但它不处理这些异常,而是将处理的责任交给调用它的方法。
try-catch 和 throws 是处理异常的两种方式:
try-catch:在当前方法内部处理异常。throws:将异常向上抛,由调用者处理。
示例:
import java.io.IOException;
public class ThrowsExample {
public static void main(String[] args) {
try {
readFile(); // main 方法调用 readFile,必须处理其抛出的异常
} catch (IOException e) {
System.out.println("main 方法捕获到异常并进行处理。");
e.printStackTrace();
}
}
// readFile 方法声明它可能会抛出 IOException
public static void readFile() throws IOException {
// 假设这里有一个文件操作可能会失败
throw new IOException("这是一个文件操作异常。");
}
}
最佳实践和注意事项
- 只捕获你知道如何处理的异常:不要使用一个巨大的
catch (Exception e)来捕获所有异常,这会掩盖真正的问题。 - 将具体的异常放在前面:在多重
catch中,子类异常必须先于父类异常被捕获。 - 不要忽略异常:在
catch块中至少要打印日志或进行一些处理,空的catch块是最糟糕的做法之一。 - 优先使用
try-with-resources:对于所有实现了AutoCloseable的资源(如文件流、数据库连接、网络连接等),都应该使用try-with-resources。 - 避免在
finally块中抛出异常:如果在finally块中抛出了异常,它会覆盖原始异常,导致原始异常信息丢失。 - 记录异常信息:在生产环境中,使用日志框架(如 SLF4J, Log4j)将异常信息记录到日志文件中,而不是仅仅打印到控制台。
- 提供有意义的错误信息:向用户展示友好的错误提示,但向开发者记录详细的堆栈信息。
希望这份详细的讲解能帮助你完全掌握 Java 的 try-catch 用法!
