杰瑞科技汇

java try catch 用法

目录

  1. 什么是异常?
  2. 为什么需要 try-catch
  3. try-catch 的基本语法
  4. try-catch-finally 结构
  5. try-catch-catch(多重 catch
  6. try-with-resources (Java 7+)
  7. throws 关键字与 try-catch 的关系
  8. 最佳实践和注意事项

什么是异常?

在 Java 中,异常是程序在运行时出现的不正常事件,它会中断正常的指令流程,Java 使用面向对象的方式来处理异常,所有异常类都是 java.lang.Throwable 类的子类。

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

Throwable 有两个主要的子类:

  • Error (错误):由 JVM(Java 虚拟机)系统内部错误或资源耗尽引起。OutOfMemoryErrorStackOverflowError,这类错误通常是致命的,我们无法在代码中处理它,只能尽量避免。
  • Exception (异常):由程序逻辑、环境问题等引起的问题,是我们可以在代码中进行捕获和处理的,它又分为两类:
    • 受检异常:编译器会强制要求你处理(要么用 try-catch 捕获,要么用 throws 声明抛出)。IOException(文件读写错误)、SQLException(数据库操作错误)。
    • 非受检异常:编译器不强制要求处理,通常是由程序逻辑错误引起的。NullPointerException(空指针异常)、ArrayIndexOutOfBoundsException(数组越界异常)、ArithmeticException(算术异常,如除以零)。

我们通常所说的 try-catch 处理的是 Exception 及其子类。


为什么需要 try-catch

没有异常处理机制,当程序发生错误时,会立即终止,并向用户显示一串难懂的堆栈跟踪信息,用户体验极差,使用 try-catch 的好处:

  • 增强程序的健壮性:程序不会因为一个意外错误而崩溃,可以优雅地处理错误。
  • 提供友好的错误提示:可以向用户展示易于理解的错误信息,而不是一堆技术术语。
  • 便于调试和维护:通过捕获异常,可以记录错误日志,帮助开发者定位问题。
  • 分离业务逻辑和错误处理:将可能出错的代码(如文件操作、网络请求)和错误处理逻辑分离开,使代码更清晰。

try-catch 的基本语法

这是最简单的用法,用于捕获一种异常。

java try catch 用法-图2
(图片来源网络,侵删)
try {
    // 可能会抛出异常的代码块
    // JVM 在这里监视代码的执行
} catch (ExceptionType1 e) {
    // 当 try 块中发生 ExceptionType1 类型的异常时,执行这里的代码
    // e 是异常对象,包含了错误信息、堆栈跟踪等
}

工作原理:

  1. JVM 执行 try 块中的代码。
  2. try 块中的代码没有发生任何异常,catch 块将被忽略,程序继续执行 try-catch 结构之后的代码。
  3. try 块中的代码发生了异常,JVM 会立即中断 try 块的执行,并寻找与该异常类型匹配的 catch 块。
  4. 如果找到了匹配的 catch 块,程序就跳转到该 catch 块中执行。
  5. 执行完 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 块是可选的,但它包含的代码无论是否发生异常,也无论异常是否被捕获,都会被执行

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

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 块的特殊情况:

如果在 trycatch 块中执行了 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-catchthrows 是处理异常的两种方式:

  • 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("这是一个文件操作异常。");
    }
}

最佳实践和注意事项

  1. 只捕获你知道如何处理的异常:不要使用一个巨大的 catch (Exception e) 来捕获所有异常,这会掩盖真正的问题。
  2. 将具体的异常放在前面:在多重 catch 中,子类异常必须先于父类异常被捕获。
  3. 不要忽略异常:在 catch 块中至少要打印日志或进行一些处理,空的 catch 块是最糟糕的做法之一。
  4. 优先使用 try-with-resources:对于所有实现了 AutoCloseable 的资源(如文件流、数据库连接、网络连接等),都应该使用 try-with-resources
  5. 避免在 finally 块中抛出异常:如果在 finally 块中抛出了异常,它会覆盖原始异常,导致原始异常信息丢失。
  6. 记录异常信息:在生产环境中,使用日志框架(如 SLF4J, Log4j)将异常信息记录到日志文件中,而不是仅仅打印到控制台。
  7. 提供有意义的错误信息:向用户展示友好的错误提示,但向开发者记录详细的堆栈信息。

希望这份详细的讲解能帮助你完全掌握 Java 的 try-catch 用法!

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