杰瑞科技汇

try catch java用法

什么是异常?

在开始之前,我们先理解什么是“异常”。

try catch java用法-图1
(图片来源网络,侵删)

在程序运行过程中,经常会遇到各种错误,

  • 算术异常:除以零(10 / 0)。
  • 空指针异常:尝试在一个为 null 的对象上调用方法(str.length(),但 strnull)。
  • 数组越界异常:访问数组中不存在的索引(arr[10],但数组长度只有 5)。
  • 文件未找到异常:尝试读取一个不存在的文件。

这些错误在 Java 中被称为“异常”(Exception),它们是程序运行时发生的不正常事件,会中断程序的正常流程。

try-catch 的作用就是捕获并处理这些异常,防止程序因此直接崩溃,让程序能够优雅地处理错误情况。


try-catch 的基本语法结构

try-catch 语句由一个 try 块和一个或多个 catch 块组成。

try catch java用法-图2
(图片来源网络,侵删)
try {
    // 可能会抛出异常的代码块
    // JVM 在这里监控代码的执行
} catch (ExceptionType1 e1) {
    // 当 try 块中发生 ExceptionType1 类型的异常时,执行这里的代码
    // e1 是一个异常对象,包含了异常的信息
} catch (ExceptionType2 e2) {
    // 当 try 块中发生 ExceptionType2 类型的异常时,执行这里的代码
} finally {
    // 可选部分
    // 无论是否发生异常,这里的代码都会被执行
    // 通常用于资源释放,如关闭文件、数据库连接等
}

语法要点:

  1. try:将可能抛出异常的代码用 括起来。
  2. catchtry 块后面必须跟至少一个 catch 块。catch 块用来捕获特定类型的异常。
    • ExceptionType:要捕获的异常类型(NullPointerException, ArrayIndexOutOfBoundsException)。
    • e (或任意变量名):一个异常类型的变量,用于接收捕获到的异常对象。
  3. finally:可选的,无论 try 块中的代码是否发生异常,也无论 catch 块是否被执行,finally 块中的代码最终都会被执行,这是进行“清理”操作(如关闭文件流、释放数据库连接)的理想位置。

工作原理

try-catch 的工作流程非常直观:

  1. 执行 try:程序开始执行 try 块中的代码。
  2. 监控异常:JVM 在 try 块内严密监控。
  3. 发生异常:如果在 try 块的某一行代码发生了异常,JVM 会立即:
    • 创建一个对应异常类的对象new ArithmeticException())。
    • 立即终止 try 块中剩余代码的执行
    • 将异常对象依次传递给 catch,进行匹配。
  4. 匹配 catch:JVM 会从上到下检查 catch 块,寻找第一个参数类型与异常对象类型匹配的 catch 块。
    • 匹配成功:执行该 catch 块中的代码,执行完毕后,程序会跳过所有其他 catch 块和 try 块的剩余部分,继续执行 finally 块(如果有的话),然后执行 try-catch 结构之后的代码。
    • 匹配失败:如果没有任何一个 catch 块能匹配该异常,异常会被“抛出”到上层调用者,或者如果是在 main 方法中未被捕获,程序将终止并打印错误堆栈信息。
  5. 未发生异常try 块中的代码全部正常执行完毕,没有发生任何异常,程序会直接跳过所有 catch 块,执行 finally 块(如果有的话),然后继续执行 try-catch 结构之后的代码。

代码示例

示例 1:捕获单个异常

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)
程序继续执行...

分析a / b 导致了 ArithmeticExceptiontry 块执行中断,程序跳转到 catch (ArithmeticException e) 块执行,之后,程序继续执行 try-catch 之后的代码,没有崩溃。

try catch java用法-图3
(图片来源网络,侵删)

示例 2:捕获多个异常(多个 catch 块)

public class MultipleCatchExample {
    public static void main(String[] args) {
        String str = null;
        int[] numbers = {1, 2, 3};
        try {
            // 可能抛出 NullPointerException
            System.out.println("字符串长度: " + str.length());
            // 可能抛出 ArrayIndexOutOfBoundsException
            System.out.println("数组元素: " + numbers[5]);
        } catch (NullPointerException e) {
            System.out.println("捕获到空指针异常:字符串对象为空!");
            e.printStackTrace();
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("捕获到数组越界异常:访问了不存在的索引!");
            e.printStackTrace();
        }
        System.out.println("程序继续执行...");
    }
}

输出:

捕获到空指针异常:字符串对象为空!
java.lang.NullPointerException: Cannot invoke "String.length()" because "str" is null
    at MultipleCatchExample.main(MultipleCatchExample.java:8)
程序继续执行...

分析str.length() 先执行并抛出了 NullPointerExceptiontry 块立即中断,程序匹配到第一个 catch 块 (NullPointerException) 并执行它。numbers[5] 这行代码根本不会被执行。catch 块的顺序很重要,应该把更具体的异常放在前面。

示例 3:finally 块的使用

finally 块通常用于释放资源,比如关闭文件流或数据库连接,确保资源一定被回收。

import java.io.FileInputStream;
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 (IOException e) {
            System.out.println("捕获到IO异常:文件可能不存在或无法读取。");
            e.printStackTrace();
        } finally {
            // 无论是否发生异常,这里的代码都会执行
            System.out.println("进入 finally 块,准备关闭文件流。");
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    System.out.println("关闭文件流时出错。");
                }
            }
        }
        System.out.println("程序继续执行...");
    }
}

输出:

捕获到IO异常:文件可能不存在或无法读取。
java.io.FileNotFoundException: non_existent_file.txt (系统找不到指定的文件。)
    at java.base/java.io.FileInputStream.open0(Native Method)
    at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
    at java.base/java.io.FileInputStream.<init>(FileInputStream.java:315)
    at FinallyExample.main(FinallyExample.java:9)
进入 finally 块,准备关闭文件流。
程序继续执行...

分析:即使发生了 IOExceptionfinally 块依然被执行,这保证了 fis.close() 会被尝试执行,避免了资源泄漏。


最佳实践

  1. 捕获具体的异常:永远不要只捕获 ExceptionThrowable,这会掩盖掉所有问题,并且难以定位,应该尽可能捕获具体的异常类型(如 NullPointerException, IOException)。

    // 不推荐
    try { ... } catch (Exception e) { ... }
    // 推荐
    try { ... } catch (FileNotFoundException e) { ... } catch (IOException e) { ... }
  2. 不要“吃掉”异常:在 catch 块中,仅仅打印一条信息然后继续执行可能不是最好的做法,至少应该打印出完整的堆栈信息 e.printStackTrace(),或者使用日志框架(如 SLF4J, Log4j2)记录日志。

  3. finally 块中释放资源:这是 finally 块最重要的用途,现代 Java 也引入了 try-with-resources 语句,这是更推荐的方式。

  4. 理解 try-with-resources (Java 7+): 对于实现了 AutoCloseable 接口的对象(如文件流、数据库连接等),可以使用 try-with-resources 语句,它会自动在 try 块结束时调用 close() 方法,无需手动 finally 块。

    // 推荐用法:try-with-resources
    try (FileInputStream fis = new FileInputStream("file.txt")) {
        // 使用 fis 读取文件
        // ...
    } catch (IOException e) {
        e.printStackTrace();
    }
    // fis.close() 会在这里被自动调用,即使发生异常

try-catch-finally 的返回值问题

一个常见的问题是:trycatchfinally 块中有 return 语句,程序的返回值是什么?

规则finally 块的执行优先级最高。finally 块中有 return 语句,它将覆盖trycatch 块中的 return

public class FinallyReturnExample {
    public static int test() {
        int result = 0;
        try {
            result = 10;
            // return result; // 如果在这里返回,result 值是 10
            throw new RuntimeException();
        } catch (Exception e) {
            result = 20;
            // return result; // 如果在这里返回,result 值是 20
        } finally {
            result = 30;
            // return result; // finally 中的 return 会覆盖所有
        }
        return result; // 这行永远不会被执行,finally 中没有 return
    }
    public static void main(String[] args) {
        System.out.println(test()); // 输出 30
    }
}

finally 块中的 return 语句是“杀手级”的,它会强制返回,使得之前的 return 失效。强烈建议在 finally 块中避免使用 return 语句,除非你清楚地知道自己在做什么。

分享:
扫描分享到社交APP
上一篇
下一篇