杰瑞科技汇

java异常try catch

什么是异常?

在程序运行过程中,发生的一些不正常的事件,它会中断程序的正常执行流程,这些事件就是异常。

  • 算术异常int a = 10 / 0; // 除以零
  • 空指针异常String str = null; str.length(); // 在一个 null 对象上调用方法
  • 数组越界异常int[] arr = new int[3]; arr[3] = 5; // 访问不存在的数组索引

如果没有异常处理机制,程序一旦遇到这些错误就会立即崩溃(终止执行),而 try-catch 机制允许我们优雅地处理这些错误,让程序在出错后依然可以继续执行或者给出友好的提示。


try-catch 的基本语法

try-catch 语句的基本结构如下:

try {
    // 可能会抛出异常的代码块
    // JVM 在这里监视代码的执行
} catch (异常类型1 e) {
    // 当 try 块中发生 异常类型1 的异常时,执行的代码块
    // e 是一个异常对象,包含了异常的信息
} catch (异常类型2 e) {
    // 当 try 块中发生 异常类型2 的异常时,执行的代码块
} finally {
    // 无论是否发生异常,也无论是否被 catch 捕获,此代码块总会执行
    // 通常用于资源释放(如关闭文件、数据库连接等)
}

工作流程:

  1. 执行 try:程序开始执行 try 块中的代码。
  2. 发生异常:如果在 try 块的执行过程中,某个语句抛出了一个异常,程序会立即跳出 try 块。
  3. 匹配 catch:JVM 会开始检查后面的 catch 块,从上到下寻找一个与抛出的异常类型匹配catch 块。
    • 匹配规则catch 块中声明的异常类型与抛出的异常类型相同,或者是其父类
  4. 执行 catch:一旦找到匹配的 catch 块,程序就会执行该 catch 块中的代码,执行完毕后,程序会继续执行 try-catch-finally 结构后面的代码。
  5. 未发生异常try 块中的所有代码都正常执行完毕,没有抛出任何异常,那么所有的 catch 块都会被忽略,程序会直接执行 finally 块(如果存在)以及其后的代码。
  6. finallyfinally 块是可选的,但它保证总会被执行,它是进行清理工作的理想位置,比如关闭文件流、网络连接等。

代码示例

示例 1:基本的 try-catch

这个例子处理一个可能除以零的异常。

public class BasicTryCatch {
    public static void main(String[] args) {
        int a = 10;
        int b = 0;
        int result = 0;
        System.out.println("开始计算...");
        try {
            // 这行代码可能会抛出 ArithmeticException
            result = a / b;
            System.out.println("计算结果是: " + result); // 这行代码不会执行
        } catch (ArithmeticException e) {
            // 当发生 ArithmeticException 时,执行这里的代码
            System.out.println("捕获到异常: 不能除以零!");
            // e.printStackTrace() 会打印出异常的详细跟踪信息,非常有助于调试
            e.printStackTrace();
        }
        System.out.println("程序执行完毕,没有崩溃。");
    }
}

输出:

开始计算...
捕获到异常: 不能除以零!
java.lang.ArithmeticException: / by zero
    at BasicTryCatch.main(BasicTryCatch.java:10)
程序执行完毕,没有崩溃。

分析:

  1. 程序打印 "开始计算..."。
  2. 进入 try 块,执行 result = a / b;
  3. 由于 b 为 0,抛出 ArithmeticException
  4. 程序立即跳过 try 块中剩余的代码。
  5. catch (ArithmeticException e) 块捕获到这个异常,打印提示信息并调用 e.printStackTrace()
  6. catch 块执行完毕,程序继续执行 System.out.println("程序执行完毕...");,程序正常结束。

示例 2:多个 catch

一个 try 块后面可以跟多个 catch 块,用来处理不同类型的异常。

重要提示: catch 块的顺序很重要,应该将更具体的异常放在前面,将更通用的异常(父类)放在后面,否则,编译器会报错。

public class MultipleCatch {
    public static void main(String[] args) {
        try {
            // 假设我们从数组中获取一个元素,然后调用其方法
            String[] names = { "Alice", "Bob" };
            String name = names[5]; // 抛出 ArrayIndexOutOfBoundsException
            int length = name.length(); // 如果上面不抛出异常,这里可能抛出 NullPointerException
        } catch (ArrayIndexOutOfBoundsException e) {
            // 先捕获更具体的异常
            System.out.println("数组索引越界了!请检查数组大小。");
            e.printStackTrace();
        } catch (NullPointerException e) {
            // 再捕获另一个具体的异常
            System.out.println("对象为空,无法调用方法!");
            e.printStackTrace();
        } catch (Exception e) {
            // 最后捕获所有异常的父类,作为最后的保障
            System.out.println("发生了未知错误!");
            e.printStackTrace();
        }
        System.out.println("程序继续执行...");
    }
}

输出:

数组索引越界了!请检查数组大小。
java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 2
    at MultipleCatch.main(MultipleCatch.java:8)
程序继续执行...

示例 3:finally

finally 块用于确保无论是否发生异常,某些代码都必须执行,最常见的用途是资源释放

import java.io.FileWriter;
import java.io.IOException;
public class FinallyExample {
    public static void main(String[] args) {
        FileWriter writer = null; // 在 try 块外声明,以便 finally 块可以访问
        try {
            writer = new FileWriter("test.txt");
            writer.write("Hello, Java!");
            // 模拟一个可能发生的异常
            // int a = 10 / 0;
            System.out.println("文件写入成功。");
        } catch (IOException e) {
            System.out.println("捕获到 IO 异常!");
            e.printStackTrace();
        } finally {
            // 这里的代码一定会执行
            System.out.println("进入 finally 块,准备关闭文件流。");
            if (writer != null) {
                try {
                    writer.close(); // 释放资源
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println("程序执行完毕。");
    }
}

输出(即使没有发生异常):

文件写入成功。
进入 finally 块,准备关闭文件流。
程序执行完毕。

输出(如果发生异常):

捕获到 IO 异常!
java.lang.ArithmeticException: / by zero
    at FinallyExample.main(FinallyExample.java:12)
进入 finally 块,准备关闭文件流。
程序执行完毕。

可以看到,无论 try 块中是否发生异常,finally 块中的代码都被执行了,确保了 writer.close() 被调用。


异常的层次结构

理解 Java 的异常类层次结构对于正确使用 try-catch 至关重要。

java.lang.Object
    java.lang.Throwable
        java.lang.Error (错误)
            - 通常由 JVM 或底层硬件问题引起,程序无法处理,如 OutOfMemoryError, StackOverflowError
        java.lang.Exception (异常)
            - IOException (及其子类,如 FileNotFoundException)
            - SQLException
            - RuntimeException (及其子类,如 NullPointerException, ArithmeticException, ArrayIndexOutOfBoundsException)
                - 这类异常也叫“非受检异常”(Unchecked Exception),编译器不强制要求捕获

受检异常 vs. 非受检异常

类型 定义 特点 处理方式
受检异常 Exception 类中除了 RuntimeException 及其子类以外的所有异常。 编译器会强制要求处理,要么用 try-catch 捕获,要么用 throws 关键字声明抛出给上层调用者处理。 必须处理。
非受检异常 包括 RuntimeException 及其所有子类,以及 Error 类及其子类。 编译器不强制要求处理,通常是由程序逻辑错误引起的,应该修正代码而不是捕获它。 可选处理,但推荐修复代码。

FileReader 构造函数会抛出 FileNotFoundException(受检异常),你必须处理它。

// 必须处理受检异常
try {
    FileReader reader = new FileReader("non_existent_file.txt");
} catch (FileNotFoundException e) {
    System.out.println("文件未找到,请检查路径。");
}

NullPointerException 是非受检异常,编译器不会提醒你,但你应该通过检查代码来避免它。


try-with-resources (Java 7+ 的新特性)

对于需要手动关闭的资源(如 FileInputStream, Connection, Socket等),Java 7 引入了一个非常方便的语法糖:try-with-resources

只要一个资源实现了 AutoCloseable 接口(所有 Java I/O 相关的类都实现了它),你就可以在 try 语句中声明它。try 块执行完毕后(无论是否发生异常),这些资源都会被自动关闭,无需 finally 块。

示例:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class TryWithResources {
    public static void main(String[] args) {
        // try-with-resources 语法
        // BufferedReader 会在 try 块结束后自动关闭
        try (BufferedReader reader = new BufferedReader(new FileReader("test.txt"))) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            System.out.println("读取文件时发生错误。");
            e.printStackTrace();
        }
        // 这里不需要 finally 块来手动关闭 reader,它已经被自动关闭了
        System.out.println("文件读取完毕,资源已自动释放。");
    }
}

优点:

  • 代码更简洁:省去了冗长的 finally 块。
  • 更安全:即使 try 块中发生异常,资源也能被正确关闭。
  • 可以同时管理多个资源:用分号隔开即可,它们会按照声明的相反顺序关闭。
try (FileInputStream fis = new FileInputStream("file1.txt");
     FileOutputStream fos = new FileOutputStream("file2.txt")) {
    // ...
} catch (IOException e) {
    // ...
}
// fis 先被关闭,fos 才被关闭
关键点 描述
try 放置可能抛出异常的代码。
catch 捕获并处理特定类型的异常,可以有多个,顺序很重要(具体到通用)。
finally 保证总会执行的代码块,用于资源清理。
异常类型 Error (不处理), Exception (受检, 必须处理), RuntimeException (非受检, 最好修复代码)。
try-with-resources 现代、自动化的资源管理方式,推荐使用。

掌握 try-catch 是编写健壮、可靠的 Java 程序的必备技能,它能让你的程序在面对意外情况时更加从容。

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