杰瑞科技汇

java try catch用法

try-catch 是 Java 异常处理机制的核心部分,它允许你编写能够优雅地处理运行时错误的代码,而不是让程序突然崩溃。

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

为什么需要 try-catch

在程序运行过程中,可能会发生各种意想不到的错误,我们称之为异常

  • 文件不存在:当你尝试读取一个不存在的文件时。
  • 网络连接中断:当你正在从网络下载数据时,网络突然断开。
  • 数学错误:当你尝试除以零时(ArithmeticException)。
  • 无效数据格式:当你尝试将一个非数字的字符串(如 "abc")转换为整数时(NumberFormatException)。

如果没有异常处理机制,程序一旦遇到这些错误,就会立即终止,并打印出一个丑陋的错误堆栈信息,给用户带来很差的体验。

try-catch 的作用就是捕获这些可能发生的异常,并在 catch 块中编写相应的处理逻辑,让程序能够从错误中恢复或者至少能够优雅地退出。


try-catch 的基本语法

最简单的 try-catch 结构如下:

java try catch用法-图2
(图片来源网络,侵删)
try {
    // 可能会抛出异常的代码块
    // JVM会在这里监控代码的执行
} catch (异常类型1 异常对象名) {
    // 当try块中发生异常类型1时,执行这里的代码
    // 你可以在这里处理异常,比如打印日志、给用户友好提示等
} catch (异常类型2 异常对象名) {
    // 当try块中发生异常类型2时,执行这里的代码
}

工作流程:

  1. 执行 try:程序开始执行 try 块中的代码。
  2. 检查异常:如果在 try 块的执行过程中没有发生任何异常,那么程序会跳过所有的 catch 块,继续执行 try-catch 结构后面的代码。
  3. 捕获异常:如果在 try 块的某一行代码发生了异常,程序会立即中断 try 块中剩余代码的执行。
  4. 匹配 catch:程序会依次检查 catch 块,寻找一个能够处理该异常类型的 catch 块,这个匹配是基于异常类型的继承关系的(子类异常可以匹配父类 catch 块)。
  5. 执行 catch:一旦找到匹配的 catch 块,程序就执行该 catch 块中的代码。
  6. 继续执行:执行完 catch 块后,程序会继续执行 try-catch 结构后面的代码,而不会终止。

代码示例

示例 1:捕获单个异常

public class TryCatchExample {
    public static void main(String[] args) {
        System.out.println("程序开始...");
        int[] numbers = {1, 2, 3};
        try {
            // 尝试访问一个不存在的数组索引
            System.out.println("尝试访问第4个元素: " + numbers[3]);
            // 这一行代码会抛出 ArrayIndexOutOfBoundsException
            System.out.println("这行代码不会被执行。");
        } catch (ArrayIndexOutOfBoundsException e) {
            // 捕获到数组越界异常
            System.out.println("捕获到异常: 数组索引越界!");
            // e 是异常对象,可以打印出详细的错误信息,方便调试
            e.printStackTrace();
        }
        System.out.println("程序继续执行...");
    }
}

输出结果:

程序开始...
尝试访问第4个元素: 捕获到异常: 数组索引越界!
java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
    at TryCatchExample.main(TryCatchExample.java:8)
程序继续执行...

分析:

  1. 程序打印 "程序开始..."。
  2. 进入 try 块,打印 "尝试访问第4个元素: "。
  3. 执行 numbers[3],因为数组 numbers 的最大索引是 2,所以抛出 ArrayIndexOutOfBoundsException
  4. try 块立即中断,跳过 System.out.println("这行代码不会被执行。");
  5. 程序开始检查 catch 块,发现 ArrayIndexOutOfBoundsException 匹配,于是进入 catch 块。
  6. 打印 "捕获到异常: 数组索引越界!"。
  7. e.printStackTrace() 打出了标准的错误堆栈信息,这对于开发者调试非常有用。
  8. catch 块执行完毕,程序继续执行 try-catch 后面的代码,打印 "程序继续执行..."。

try-catch-finally 结构

在很多情况下,无论是否发生异常,我们都希望执行一些“清理”工作,比如关闭文件、释放数据库连接、释放网络资源等,这时就需要 finally 块。

finally 块中的代码无论是否发生异常,也无论 catch 块是否执行,都会被执行

try {
    // 可能会抛出异常的代码
} catch (异常类型 异常对象名) {
    // 处理异常
} finally {
    // 无论try或catch如何结束,这里的代码一定会被执行
    // 通常用于资源释放
}

示例 2:try-catch-finally 的使用

import java.io.FileInputStream;
import java.io.IOException;
public class TryCatchFinallyExample {
    public static void main(String[] args) {
        FileInputStream fis = null; // 定义在try外,以便finally可以访问
        try {
            fis = new FileInputStream("non_existent_file.txt");
            // 如果文件不存在,会抛出 FileNotFoundException
            int data = fis.read();
            System.out.println("读取到数据: " + data);
        } catch (IOException e) {
            // IOException 是 FileNotFoundException 的父类
            System.out.println("捕获到IO异常: 文件可能不存在或读取失败。");
            e.printStackTrace();
        } finally {
            // 无论是否发生异常,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)
    ...
进入finally块,准备关闭文件流。
程序执行完毕。

分析:

  1. try 块中尝试打开一个不存在的文件,抛出 IOException
  2. catch 块捕获异常并处理。
  3. 无论 catch 是否执行,finally 块都会被无条件执行,它尝试关闭文件流(尽管文件打开失败,fis 仍为 null,但逻辑上是安全的)。
  4. 程序继续执行 finally 块后面的代码。

try-with-resources (Java 7+ 推荐)

在 Java 7 中,引入了一个非常方便的特性:try-with-resources,它极大地简化了资源管理(如文件流、数据库连接、网络连接等)。

规则: 任何实现了 AutoCloseable 接口的资源都可以在 try 语句中声明,当 try 块执行完毕(无论是否发生异常),这些资源会自动被关闭,无需手动在 finally 块中关闭。

示例 3:使用 try-with-resources

import java.io.FileInputStream;
import java.io.IOException;
public class TryWithResourcesExample {
    public static void main(String[] args) {
        // 将资源的声明放在 try 后面的括号中
        try (FileInputStream fis = new FileInputStream("non_existent_file.txt")) {
            int data = fis.read();
            System.out.println("读取到数据: " + data);
        } catch (IOException e) {
            System.out.println("捕获到IO异常: " + e.getMessage());
            e.printStackTrace();
        }
        // 不需要 finally 块来关闭 fis!
        // try-with-resources 会自动调用 fis.close()
        System.out.println("程序执行完毕,文件流已自动关闭。");
    }
}

优点:

  • 代码简洁:消除了冗余的 finally 块。
  • 更安全:即使 try 块中抛出异常,close() 方法也会被调用,避免了资源泄漏。
  • 可同时管理多个资源:用分号隔开即可,try (Resource1 r1 = ...; Resource2 r2 = ...) { ... }

多重 catch

一个 try 块后面可以跟多个 catch 块,用于处理不同类型的异常。需要注意的是,子类异常必须放在父类异常的前面,否则会导致编译错误。

示例 4:多重 catch

public class MultipleCatchExample {
    public static void main(String[] args) {
        String str = "abc";
        try {
            int num = Integer.parseInt(str); // 抛出 NumberFormatException
            System.out.println(num / 0);    // 如果上面成功,这里会抛出 ArithmeticException
        } catch (NumberFormatException e) {
            System.out.println("捕获到数字格式异常: 字符串无法转换为整数。");
        } catch (ArithmeticException e) {
            System.out.println("捕获到算术异常: 不能除以零。");
        } catch (Exception e) {
            // 捕获所有其他未处理的异常,作为最后的保障
            System.out.println("捕获到未知异常: " + e.getMessage());
        }
    }
}

输出结果:

捕获到数字格式异常: 字符串无法转换为整数。

分析:

  1. Integer.parseInt("abc") 抛出了 NumberFormatException
  2. 程序立即中断,开始检查 catch 块。
  3. 第一个 catch (NumberFormatException e) 匹配成功,执行其内部代码。
  4. 后面的 catch 块被跳过。

如果顺序错误:

// 错误的顺序
try {
    // ...
} catch (Exception e) { // Exception 是所有异常的父类
    // ...
} catch (NumberFormatException e) { // 这行代码永远不会被执行,因为上面的Exception已经捕获了所有异常
    // ...
}
// 编译器会报错:Unreachable catch block for NumberFormatException. It is already handled by the catch block for Exception

throws 关键字(与 try-catch 的关系)

try-catch处理异常的一种方式,而 throws声明异常的一种方式。

当一个方法内部可能发生一个受检异常(Checked Exception,如 IOException),但它本身不想处理时,可以使用 throws 关键字将这个异常“抛”给它的调用者,由调用者来决定如何处理(要么 try-catch,要么继续 throws)。

import java.io.IOException;
public class ThrowsExample {
    public static void readFile() throws IOException {
        // 假设这里有一个文件操作可能会抛出IOException
        // 我不想在这里处理,所以声明 throws IOException
        throw new IOException("文件读取失败");
    }
    public static void main(String[] args) {
        try {
            readFile(); // main方法调用readFile,必须处理它声明的IOException
        } catch (IOException e) {
            System.out.println("在main方法中捕获到文件异常并进行处理。");
            e.printStackTrace();
        }
    }
}

throws vs try-catch

特性 try-catch throws
作用 处理异常 声明异常,不处理
位置 在方法体内部 在方法签名后面
责任 由当前方法负责处理异常 将处理责任交给方法的调用者
使用场景 适合能够恢复或需要特定处理的异常 适合无法在当前方法处理,或希望调用者知晓的异常(尤其是受检异常)

结构 语法 用途
try-catch try { ... } catch (ExceptionType e) { ... } 捕获并处理单个异常。
try-catch-catch try { ... } catch (E1 e) { ... } catch (E2 e) { ... } 捕获并处理多种不同类型的异常。
try-catch-finally try { ... } catch (E e) { ... } finally { ... } 捕获异常,并确保无论是否发生异常,finally 块中的清理代码都会执行。
try-with-resources try (Resource r = ...) { ... } 自动管理实现了 AutoCloseable 的资源,推荐在 Java 7+ 中使用。
throws method() throws ExceptionType 声明方法可能抛出的异常,将处理责任转移给调用者。

掌握 try-catch 及其相关结构是编写健壮、可靠 Java 程序的必备技能,好的异常处理能让你的程序更稳定,用户体验也更好。

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