在Java语言里,异常是程序运行过程中出现的非正常情况,比如读取不存在的文件、网络突然断开或者数组越界。如果不对这类情况做处理,线程会直接终止并把堆栈信息打印到控制台,严重的会让整个应用崩溃。try-catch语句是Java异常处理最核心的结构,它允许开发者把可能出错的代码隔离出来,并在异常发生时给出补救方案。

一、try-catch的基础语法
try-catch的最简形式由一个try块和一个或多个catch块组成。try块中放置需要监控的普通业务代码,catch块声明它能够捕获的异常类型以及对应的处理变量。当try块中的代码抛出了与catch声明类型匹配(或是其子类)的异常对象时,虚拟机就会中断try块中剩余语句的执行,转而进入该catch块。
从语法约束上看,try后面必须紧跟一对花括号,不能单独存在;catch后面的括号里要写“异常类型 变量名”,这个变量只在当前catch块内有效。如果需要捕获多种异常,可以写成多个catch块,也可以使用Java 7引入的多异常捕获语法,用竖线分隔类型。下面是一段最基础的示例:
public class BasicTryCatch {
public static void main(String[] args) {
try {
int a = 10;
int b = 0;
// 这里会抛出ArithmeticException
int result = a / b;
System.out.println(result);
} catch (ArithmeticException e) {
System.out.println("发生算术异常:" + e.getMessage());
}
System.out.println("程序继续运行");
}
}
上述代码在try块中进行了除以零的操作,JVM会构造一个ArithmeticException对象并抛出。由于后面紧跟的catch声明了ArithmeticException,控制流立即跳入该catch块打印提示,随后继续执行try-catch结构之外的输出语句。可以看到,异常被捕获后程序并没有终止,而是平稳地走到了最后。
二、try-catch的执行流程
理解执行流程是写好异常处理的前提。当方法执行到try块时,正常语句会按顺序执行;一旦某条语句抛出异常,这条语句之后的try块代码就不再运行。JVM从抛点开始,向上查找第一个“异常类型兼容”的catch块。兼容是指抛出的异常对象是catch声明类型的实例,或者为其子类对象。
如果try块顺利执行完且没有异常,那么所有catch块都会被跳过,直接运行整个try-catch之后的代码。如果异常没有被任何catch捕获,它就会沿着调用栈继续向外抛,直到被更外层的方法处理或导致线程结束。下面的例子展示了多个catch的匹配顺序:
public class MultiCatchFlow {
public static void main(String[] args) {
try {
String text = null;
// 此处会抛出NullPointerException
System.out.println(text.length());
} catch (NullPointerException npe) {
System.out.println("捕获到空指针异常");
} catch (RuntimeException re) {
System.out.println("捕获到运行时异常父类");
}
}
}
在上面的流程中,text为null调用length方法触发NullPointerException。虽然NullPointerException本身也是RuntimeException的子类,但因为第一个catch已经精确匹配,所以只执行第一个catch块,第二个catch不会被执行。这提醒我们写多个catch时要把子类异常放在前面,否则会出现编译错误“已捕获的异常”问题。
2.1 带finally块的完整结构
除了try和catch,还可以加上finally块。finally中的代码无论是否发生异常、是否被捕获,都会在执行完try或catch后运行(除非调用了System.exit)。它通常用来释放数据库连接、关闭文件流等清理工作。执行次序为:try正常则走try->finally;try异常且被catch则走try->catch->finally。
public class TryCatchFinally {
public static void main(String[] args) {
try {
int[] arr = new int[2];
// 越界,抛ArrayIndexOutOfBoundsException
arr[5] = 10;
} catch (ArrayIndexOutOfBoundsException ex) {
System.out.println("数组越界已被处理");
} finally {
System.out.println("finally块始终执行,用于清理");
}
}
}
这段代码中,数组赋值越界触发异常,catch块打印处理信息,紧接着finally块无条件输出清理提示。即使catch块里再有return语句,finally也会在方法返回前执行,这是Java规范强制保证的,对资源安全管理非常关键。
三、常见误区与编写建议
初学者常犯的一个错误是catch块里只写空语句或者仅仅打印堆栈却不采取恢复动作,这会让异常被“静默吞掉”,后期排查问题极其困难。另一个误区是在catch中捕获了过于宽泛的Exception甚至Throwable,虽然不会漏异常,但也可能把不该由此处处理的系统错误(如OutOfMemoryError)拦下来,打乱程序预期。
建议实践中尽量捕获具体异常,在catch中做有意义的补偿,比如重试、使用默认值或给用户友好提示;把必须清理的逻辑放进finally或使用Java 7的try-with-resources语法。这样既能保证异常可控,又能让代码可读性更高。下面用try-with-resources展示更优的写法:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class TryWithResourcesDemo {
public static void main(String[] args) {
// 资源会在try结束后自动关闭
try (BufferedReader reader = new BufferedReader(new FileReader("test.txt"))) {
String line = reader.readLine();
System.out.println(line);
} catch (IOException e) {
System.out.println("读取文件出错:" + e.getMessage());
}
}
}
在这个例子中,BufferedReader实现了AutoCloseable接口,编译器会自动在finally位置插入关闭调用,比手写finally更安全且简洁。掌握try-catch基础语法与执行流程之后,再结合这些现代写法,就能写出健壮且易维护的Java异常处理代码。