异常机制(JAVA基础)

目录

一.CheckedException

二.RuntimeException

三.try-with-resource新特性

四.声明式解决异常

五.自定义异常


一.CheckedException

在Java中,CheckedException(检查型异常)是异常体系中的一类,与RuntimeException(运行时异常)相对。检查型异常是编译器会强制要求处理的异常,通常是由于程序员的编程错误导致的,比如I/O错误、SQL错误等。在Java中,所有继承自java.lang.Exception类(但不包括java.lang.RuntimeException及其子类)的异常都是检查型异常。

处理检查型异常通常有两种方式:

使用try-catch块:在可能出现异常的代码块周围使用try-catch块来捕获并处理异常。这是最常见的处理检查型异常的方式。

try {
    // 可能抛出检查型异常的代码
    FileInputStream fileInput = new FileInputStream("nonexistent.txt");
} catch (FileNotFoundException e) {
    // 处理异常
    System.out.println("文件未找到:" + e.getMessage());
}

在方法签名中声明抛出异常:如果方法内部可能抛出检查型异常,并且调用者需要处理这个异常,那么可以在方法签名中使用throws关键字声明抛出异常。这样,调用该方法的代码要么需要处理这个异常(使用try-catch),要么也需要声明抛出该异常。

public void readFile() throws FileNotFoundException {
    // 可能抛出检查型异常的代码
    FileInputStream fileInput = new FileInputStream("nonexistent.txt");
    // ...
}


在调用readFile方法的代码中,要么需要处理FileNotFoundException,要么继续声明抛出它:

try {
    readFile();
} catch (FileNotFoundException e) {
    // 处理异常
}

或者:

public void anotherMethod() throws FileNotFoundException {
    readFile();
    // ...
}

注意,RuntimeException及其子类(比如NullPointerException、IndexOutOfBoundsException等)是运行时异常,它们通常是由程序逻辑错误导致的,编译器不会强制要求处理这些异常。然而,最佳实践仍然建议程序员处理所有可能发生的异常,无论是检查型异常还是运行时异常。

二.RuntimeException

RuntimeException 是 Java 异常体系中的一个重要子类,它属于未检查的异常(Unchecked Exception)。与检查型异常(Exception)不同,RuntimeException 及其子类不需要在方法签名中显式声明,编译器也不会强制要求调用者处理它们。

RuntimeException 通常表示程序中的编程错误,如逻辑错误、空指针引用、数组越界等。这些错误通常是程序员可以避免的,而且通常指示了程序中的 bug。

下面是一些常见的 RuntimeException 子类:

  • NullPointerException:当应用程序试图在需要对象的地方使用 null 时抛出。
  • IndexOutOfBoundsException:当应用程序试图访问某个序列的非法索引时(如数组、字符串或列表的索引超出范围)抛出。
  • ArrayStoreException:当试图将错误类型的对象存储在某个对象数组中时抛出。
  • ClassCastException:当试图将对象强制转换为不是实例的子类时抛出。
  • IllegalArgumentException:当向方法传递非法或不适当的参数时抛出。
  • IllegalStateException:当在对象不适当的状态上调用方法时抛出。

由于 RuntimeException 是未检查的异常,它们通常用于指示编程错误,而不是可预见的异常情况(这通常是检查型异常处理的范畴)。因此,在编写代码时,如果方法内部可能出现 RuntimeException,通常不需要(也不鼓励)在方法签名中使用 throws 关键字来声明它。

然而,最佳实践仍然是在代码中尽可能处理所有可能的异常,无论是检查型异常还是运行时异常。对于 RuntimeException,通常的做法是使用 try-catch 块来捕获异常,并记录错误信息、进行恢复操作或至少向调用者传达异常的发生。示例:

public void performRiskyOperation() {
    try {
        // 这里可能会抛出 RuntimeException
        Object obj = getSomeObjectThatMightBeNull();
        obj.toString(); // 如果 obj 是 null,这里会抛出 NullPointerException
    } catch (RuntimeException e) {
        // 处理 RuntimeException
        System.err.println("发生 RuntimeException: " + e.getMessage());
        e.printStackTrace(); // 打印堆栈跟踪信息
        // 在这里可以进行一些恢复操作,或者至少通知调用者异常的发生
    }
}

private Object getSomeObjectThatMightBeNull() {
    // ... 返回的对象可能是 null
    return null;
}

在这个例子中,performRiskyOperation 方法尝试对可能为 null 的对象调用 toString 方法,这会导致 NullPointerException。通过使用 try-catch 块,我们能够捕获这个异常并处理它,而不是让异常传递到上层调用者并可能导致程序意外终止。

三.try-with-resource新特性

try-with-resources 是 Java 7 中引入的一个新特性,用于简化资源的管理和自动关闭,避免资源泄漏问题。这个特性主要针对实现了 AutoCloseable 或 Closeable 接口的资源,如文件、网络连接等。

使用方法

使用 try-with-resources 语句时,需要将需要关闭的资源放在括号中,并在 try 块中使用这些资源。当 try 块执行完毕后,无论是否有异常抛出,这些资源都会被自动关闭。

try (ResourceType resource = new ResourceType()) {
    // 使用 resource 进行操作
} catch (Exception e) {
    // 处理异常
}

工作原理

try-with-resources 的底层实现依赖于 Java 虚拟机的异常处理机制和字节码的异常表。当 try 块中的代码执行完毕或抛出异常时,Java 虚拟机会自动调用资源的 close() 方法来确保资源被正确地关闭。

优势

  • 简化代码:无需在 finally 块中手动关闭资源,使代码更加简洁。
  • 避免资源泄漏:即使在 try 块中发生异常,资源也会被正确关闭,从而避免了资源泄漏的问题。
  • 提高代码可读性:资源声明和使用都在同一个地方,使代码更加清晰易懂。

示例下面是一个使用 try-with-resources 的示例,演示如何自动关闭文件流:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class TryWithResourcesExample {
    public static void main(String[] args) {
        try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在这个示例中,BufferedReader 是一个实现了 Closeable 接口的资源。使用 try-with-resources 语句,我们无需在 finally 块中手动关闭 BufferedReader,Java 虚拟机会自动为我们完成这个工作。

四.声明式解决异常

在Java中,异常处理是一个重要的部分,它允许程序在运行时遇到问题时进行响应。传统的异常处理通常使用try-catch-finally语句来捕获和处理异常。然而,从Java 8开始,引入了新的异常处理机制,包括Optional类、try-with-resources语句和Lambda表达式等,它们都可以被视为声明式异常处理的例子。

声明式异常处理强调通过编程语型来描述“做什么”,而不是“如何做”。这意味着代码更关注于业务逻辑而不是错误处理细节。以下是Java中声明式异常处理的几个例子:

1. 使用Optional类避免空指针异常

Java 8引入了Optional类,用于避免空指针异常。通过Optional,你可以明确地表示一个值可能存在也可能不存在,从而避免在运行时抛出空指针异常。

Optional<String> optional = Optional.ofNullable(getValueThatMayBeNull());
optional.ifPresent(value -> System.out.println(value));

2. 使用try-with-resources自动关闭资源

try-with-resources语句可以确保实现了AutoCloseable或Closeable接口的资源在try块执行完毕后被自动关闭,从而避免了资源泄漏。

try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
    e.printStackTrace();
}

3. 使用函数式接口和Lambda表达式简化异常处理通过结合函数式接口和Lambda表达式,你可以将异常处理逻辑从主业务逻辑中分离出来,使得代码更加清晰。

// 定义一个函数式接口,处理可能抛出异常的操作
@FunctionalInterface
interface RiskyOperation {
    void execute() throws IOException;
}

// 使用Lambda表达式和try-catch处理异常
RiskyOperation operation = () -> {
    // 这里是可能抛出异常的代码
    throw new IOException("An error occurred");
};

try {
    operation.execute();
} catch (IOException e) {
    System.err.println("An error occurred: " + e.getMessage());
}

4. 使用异常链追踪原始异常

Java 8引入了Throwable.addSuppressed(Throwable)方法,允许你添加被抑制的异常,这样可以在捕获异常时保留原始的异常信息。

try {
    // 可能抛出异常的代码
} catch (Exception e) {
    Exception originalException = new Exception("A more specific error message");
    originalException.addSuppressed(e);
    throw originalException;
}

5. 使用CompletableFuture处理异步异常在Java 8及之后的版本中,CompletableFuture类用于异步编程。你可以使用exceptionally方法来处理异步操作中可能抛出的异常。

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    // 这里是可能抛出异常的异步代码
    throw new RuntimeException("An error occurred");
});

future.exceptionally(e -> {
    // 处理异常
    System.err.println("An error occurred: " + e.getMessage());
    return null; // 返回一个默认值或null
});

总的来说,声明式异常处理强调的是通过高级抽象和组合来简化异常处理逻辑,使代码更加清晰和易于维护。它鼓励开发者将精力集中在业务逻辑上,而不是纠缠在异常处理的细节中。

五.自定义异常

在Java中,自定义异常通常是通过创建一个继承自Exception类或其子类的新类来实现的。通过自定义异常,你可以定义特定于你的应用程序的异常类型,并在适当的时候抛出这些异常。这有助于提供更清晰的错误消息,并允许调用者以更具体的方式处理错误。下面是一个简单的示例,展示了如何在Java中创建自定义异常:

// 自定义异常类,继承自Exception
public class CustomException extends Exception {

    // 无参构造函数
    public CustomException() {
        super();
    }

    // 带有一个错误消息的构造函数
    public CustomException(String message) {
        super(message);
    }

    // 带有错误消息和原因的构造函数
    public CustomException(String message, Throwable cause) {
        super(message, cause);
    }

    // 带有原因的构造函数
    public CustomException(Throwable cause) {
        super(cause);
    }
}

在这个例子中,CustomException类继承了Exception类,并提供了几个构造函数,以便在创建异常实例时提供错误消息和/或原因。你可以根据需要添加更多的构造函数或方法。要抛出这个自定义异常,只需在适当的地方使用throw关键字,如下所示:

public void performSomeTask() throws CustomException {
    // 执行一些操作
    // ...

    // 如果出现特定情况,抛出自定义异常
    if (someCondition) {
        throw new CustomException("This is a custom exception message");
    }

    // 继续执行其他操作
    // ...
}

注意,在抛出异常的方法签名中,你需要使用throws关键字来声明可能会抛出的异常类型。这样,调用该方法的代码就知道需要处理或继续抛出这个异常。

要处理这个自定义异常,调用者可以使用try-catch块来捕获它,如下所示:

try {
    performSomeTask();
} catch (CustomException e) {
    // 处理异常
    System.err.println("Caught a custom exception: " + e.getMessage());
}


通过这种方式,你可以创建特定于你的应用程序需求的异常,并提供更详细的错误信息,从而使异常处理更加清晰和有效。


 文章制作不易,如果有帮助的话,还希望能给个点赞关注支持一下,谢谢大家!🙏🙏🙏

  • 23
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只藏羚吖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值