百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

Java中 高级的异常处理(java中异常处理的两种方式)

csdh11 2025-04-10 22:04 7 浏览

介绍

异常处理是软件开发的一个关键方面,尤其是在 Java 中,这种语言以其稳健性和平台独立性而闻名。正确的异常处理不仅可以防止应用程序崩溃,还有助于调试并向用户提供有意义的反馈。

在本文中,我们将深入研究 Java 中异常处理的高级概念,而不仅仅是基本的 try-catch 块。

了解 Java 异常层次结构

Java 的异常处理建立在异常类的层次结构上,所有异常类都派生自 java.lang.Throwable 类。该层次结构主要分为两类:错误和异常。了解这种层次结构对于在 Java 应用程序中实现有效的异常处理机制至关重要。

Throwable类

异常层次结构的顶部是Throwable类。它是 Java 中所有错误和异常的超类。只有属于此类(或其子类之一)实例的对象才能由 Java 虚拟机 (JVM) 或关键字throw抛出。

Error与Exception

Error在 Java 中和Exception之间的区别很重要:

  • 错误:这些错误不应该被应用程序捕获。错误是严重故障时发生的异常情况,JVM 无法处理这些故障。这些都是不寻常的情况,在正常情况下不太可能发生。包括OutOfMemoryErrorStackOverflowErrorAssertionError
  • 异常:这些表示合理的应用程序可能想要捕获的条件。异常进一步分为检查异常和非检查异常。

检查异常与非检查异常

  • 检查的异常:这些是编写良好的应用程序应该预见到并从中恢复的异常情况。例如,FileNotFoundException当未找到文件时发生,以及IOException在 I/O 操作失败或中断期间发生。检查的异常是在编译时检查的,这意味着编译器强制使用try-catch块处理这些异常或使用关键字throws在方法中声明它们。
  • 未经检查的异常:也称为运行时异常,其中包括编程错误,例如逻辑错误或 API 使用不当。编译时忽略运行时异常。例如,NullPointerException当尝试使用具有该null值的对象引用时会发生这种情况,还有ArrayIndexOutOfBoundsException在尝试访问具有非法索引的数组元素时会引发这种情况。

代码示例:探索异常层次结构

让我们用代码示例来演示异常层次结构:

public class ExceptionHierarchyExample {
    public static void main(String[] args) {
// 处理已检查的异常
        try {
            FileInputStream file = new FileInputStream("nonexistentfile.txt");
        } catch (FileNotFoundException e) {
            System.out.println("Checked Exception: " + e.getMessage());
        }

 // 处理未检查的异常
        try {
            int[] numbers = new int[3];
          // 这将抛出 ArrayIndexOutOfBoundsException
            int number = numbers[5]; 
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("Unchecked Exception: " + e.getMessage());
        }
    }
}

在此示例中,FileNotFoundException 是已检查异常,而
ArrayIndexOutOfBoundsException 是未检查异常。 try-catch 块演示了如何处理这些异常。


了解 Java 异常层次结构对于 Java 开发人员来说是基础。它允许您通过正确处理不同类型的异常来编写更健壮和防错的代码。掌握错误和异常之间以及检查异常和非检查异常之间的区别,使您能够设计应用程序以有效处理各种错误情况。

异常处理的最佳实践

异常处理是 Java 编程的一个基本方面,对于构建健壮、可靠和容错的应用程序至关重要。在异常处理中采用最佳实践不仅可以防止应用程序意外崩溃,还有助于诊断问题和改善用户体验。

捕获特定异常

  • 基本原理:捕获最具体的异常可能有助于处理确切的错误情况。它使代码更具可读性,并有助于根据不同的异常采取特定的操作。
  • 示例:不捕获一般异常,而是捕获特定异常,例如 IOException、SQLException 等。
try {
   // 可能抛出 IOException 的代码
} catch (IOException e) {
   //专门处理IOException
}

避免空的 Catch 块

  • 理由:空的 catch 块(也称为异常吞没)可能会使调试成为一场噩梦,因为它隐藏了错误。
  • 示例:始终以某种方式记录或处理异常。
try {
    // 可能出异常的操作
} catch (SomeException e) {
    System.err.println("Error occurred: " + e.getMessage());
}

使用 Final 块进行资源清理

  • 基本原理:无论是否抛出异常,finally 块都会执行。这使得它成为执行清理操作的理想场所,特别是释放文件处理程序、网络连接或数据库连接等资源。
  • 示例:确保资源在finally 块中关闭。
FileReader fr = null;
try {
    fr = new FileReader("file.txt");
} catch (IOException e) {
    // 处理异常
} finally {
    if (fr != null) {
        try {
            fr.close();
        } catch (IOException e) {
            // 处理关闭异常
        }
    }
}

遵循早throw、晚catch的原则

  • 基本原理:该原则意思是,一旦检测到错误,就应该抛出异常,稍后在更高的级别捕获,那里有足够的上下文来正确处理它们。
  • 示例:让低级方法抛出异常并在应用程序中的更高级别处理它们。
public void processData() throws DataProcessingException {
 // 可能抛出 DataProcessingException
}

public void higherLevelFunction() {
    try {
        processData();
    } catch (DataProcessingException e) {
        // 在更高级别的时候处理异常
    }
}

除非绝对必要,否则不要捕获 Throwable、Error 或 RuntimeException

  • 理由:捕获 Throwable 或 Error 可能会导致捕获不应改处理的严重系统错误。例如:捕获 RuntimeException 没有必要,它会掩盖 NullPointerException 等错误。
  • 示例:避免捕获非常普遍的异常或错误。
try {
// 可能抛出特定异常的代码
} catch (SpecificException e) {
// 只处理特定的异常

}

记录抛出的异常

  • 理由:记录方法可能引发的异常可以帮助其他开发人员了解他们需要处理的错误情况。
  • 示例:使用@throws 或@exception Javadoc 标记来记录异常。

在异常处理中遵循这些最佳实践可确保您的 Java 应用程序更加稳定、可靠且易于维护。正确处理异常可以提供有意义的错误信息并防止应用程序崩溃,从而改善调试过程并增强整体用户体验。

异常处理的高级技术

虽然 Java 中的基本异常处理涉及 try-catch 块和 throws 关键字,但高级技术可以进一步增强您优雅且高效地处理错误的能力。这些技术可以更精确地控制异常管理,并有助于创建更健壮和可维护的代码。

创建自定义异常

  • 理由:自定义异常可以使代码更具可读性,并有助于区分应用程序的特定错误和标准 Java 异常。当您需要向异常添加附加信息或阐明异常的目的时,它们特别有用。
  • 实现: 扩展 Exception(对于已检查的异常)或 RuntimeException(对于未检查的异常)。提供接受消息、错误原因或两者的构造函数。
public class MyCustomException extends Exception {
    public MyCustomException(String message) {
        super(message);
    }

    public MyCustomException(String message, Throwable cause) {
        super(message, cause);
    }
}

异常链

  • 基本原理:异常链(也称为异常包装)是捕获原始异常并重新抛出包含原始异常的新异常的过程。当您想要向异常添加附加上下文或将较低级别的异常转换为较高级别的异常时,这非常有用。
  • 实现:使用接受另一个异常作为原因的异常构造函数。 getCause() 方法可用于检索原始异常。
try {
// 一些可能抛出 SQLException 的代码
} catch (SQLException e) {
    throw new MyCustomException("Database operation failed", e);
}

Try-With-Resources

  • 基本原理:在 Java 7 中引入的 try-with-resources 简化了关闭实现 AutoCloseable 或 Closeable 接口的资源的过程。它确保每个资源在语句结束时关闭,这有助于防止资源泄漏。
  • 实现:try 括号内声明的资源在 try 块之后自动关闭。 可以与 catch 和/或 finally 块结合使用
try (FileInputStream fis = new FileInputStream("file.txt");
     BufferedInputStream bis = new BufferedInputStream(fis)) {

} catch (IOException e) {
  //处理异常

}

堆栈跟踪的高级使用

  • 理由:堆栈跟踪提供了有关导致异常的方法调用序列的有价值的信息。通过分析堆栈跟踪,您可以更深入地了解错误上下文。
  • 实现: 使用 Throwable 类的 getStackTrace() 方法来检索堆栈跟踪元素。 分析或记录堆栈跟踪以进行更深入的错误分析。
try {
// 一些可能抛出异常的代码
} catch (Exception e) {
    StackTraceElement[] elements = e.getStackTrace();
    for (StackTraceElement element : elements) {
        System.out.println(element);
    }
}

控制异常传播

  • 理由:在某些情况下,您可能希望控制异常如何通过您的方法传播。这可以通过捕获并重新抛出异常或策略性地使用 throws 子句来完成。
  • 实现: 使用或不使用附加处理重新抛出异常。 在方法签名的 throws 子句中声明异常。
public void someMethod() throws MyCustomException {
    try {
// 一些可能抛出异常的代码
    } catch (AnotherException e) {
        // 抛出一些异常
        throw new MyCustomException("Custom message", e);
    }
}

Java中先进的异常处理技术使开发人员能够更有效地管理错误并适应各种场景。自定义异常、异常链、try-with-resources、堆栈跟踪的复杂使用以及受控异常传播是开发人员创建弹性且可维护的 Java 应用程序的强大工具。

Java Streams 和 Lambda 中的异常处理

Java 8 引入了流和 lambda,极大地改变了开发人员编写 Java 代码的方式,尤其是在处理集合时。然而,这种范式转变带来的挑战之一是处理这些功能构造中的异常。让我们深入研究在 Java 流和 lambda 表达式的上下文中有效管理异常的策略。

处理 Lambda 表达式中的异常

Java 中的 Lambda 不允许抛出已检查异常,除非在函数式接口中显式声明它们。这种限制通常需要不同的异常处理方法。

在 Lambda 中使用 Try-Catch 块

最直接的方法是直接在 lambda 表达式内处理异常

List list = Arrays.asList("file1.txt", "file2.txt");"file1.txt", "file2.txt");
list.forEach(fileName -> {
    try {
        // throw IOException
        Path path = Paths.get(fileName);
        byte[] fileBytes = Files.readAllBytes(path);
    } catch (IOException e) {
        e.printStackTrace();
    }
});

创建包装方法

为了使代码更简洁,尤其是在多个位置处理相同类型的异常处理时,您可以创建包装方法。

public static Consumer handleCheckedExceptions(Consumer consumer) {
    return fileName -> {
        try {
            consumer.accept(fileName);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    };
}

这样使用:

list.forEach(handleCheckedExceptions(fileName -> {
//可能抛出 IOException 的代码
}));

流操作中的异常处理

处理流中的异常,特别是在中间操作(如map、filter等)中,可能很棘手,因为它们需要一个不会抛出已检查异常的函数。

使用包装器 Lambda

原理:与处理 lambda 表达式中的异常类似,您可以使用包装方法来处理流操作中的异常。

public  Function wrap(FunctionWithException function) {
    return arg -> {
        try {
            return function.apply(arg);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    };
}

@FunctionalInterface
public interface FunctionWithException {
    R apply(T t) throws Exception;
}

在流中使用:

list.stream()
    .map(wrap(fileName -> new String(Files.readAllBytes(Paths.get(fileName)))))new String(Files.readAllBytes(Paths.get(fileName)))))
    .forEach(System.out::println);

自定义功能接口

创建允许检查异常的自定义功能接口,提供在流操作中处理异常的更流畅的方式。

@FunctionalInterface
public interface ThrowingFunction {
    R apply(T t) throws E;
}

// Usage in a stream
list.stream()
    .map((ThrowingFunction) fileName -> new String(Files.readAllBytes(Paths.get(fileName))))
    .forEach(System.out::println);

处理未检查的异常

对于未经检查的异常,您可以使用 try-catch 块以通常的方式处理它们。但是,通常最好通过正确的输入验证并避免可能导致此类异常的操作来确保 lambda 和流操作不易出现运行时异常。

Java 流和 lambda 中的异常处理需要深思熟虑,特别是因为函数式接口施加的限制。通过使用包装方法、自定义函数接口或直接在 lambda 中处理异常,您可以有效地管理已检查和未检查的异常,从而生成更健壮且可读的代码。

结论

Java 中的高级异常处理是编写健壮、可维护和可调试代码的强大工具。对于任何经验丰富的 Java 开发人员来说,理解异常层次结构、遵循最佳实践、使用自定义异常和异常链等高级技术以及处理 Java 8 功能(如流和 lambda)中的异常都是至关重要的。

通过掌握这些概念,开发人员可以确保他们的 Java 应用程序优雅地处理意外情况,从而提高整体软件质量和可靠性。

如果喜欢这篇文章,点赞支持一下,关注公众号查看更多内容,微信搜索:京城小人物,关注我第一时间查看更多内容!

相关推荐

探索Java项目中日志系统最佳实践:从入门到精通

探索Java项目中日志系统最佳实践:从入门到精通在现代软件开发中,日志系统如同一位默默无闻却至关重要的管家,它记录了程序运行中的各种事件,为我们排查问题、监控性能和优化系统提供了宝贵的依据。在Java...

用了这么多年的java日志框架,你真的弄懂了吗?

在项目开发过程中,有一个必不可少的环节就是记录日志,相信只要是个程序员都用过,可是咱们自问下,用了这么多年的日志框架,你确定自己真弄懂了日志框架的来龙去脉嘛?下面笔者就详细聊聊java中常用日志框架的...

物理老师教你学Java语言(中篇)(物理专业学编程)

第四章物质的基本结构——类与对象...

一文搞定!Spring Boot3 定时任务操作全攻略

各位互联网大厂的后端开发小伙伴们,在使用SpringBoot3开发项目时,你是否遇到过定时任务实现的难题呢?比如任务调度时间不准确,代码报错却找不到方向,是不是特别头疼?如今,随着互联网业务规模...

你还不懂java的日志系统吗 ?(java的日志类)

一、背景在java的开发中,使用最多也绕不过去的一个话题就是日志,在程序中除了业务代码外,使用最多的就是打印日志。经常听到的这样一句话就是“打个日志调试下”,没错在日常的开发、调试过程中打印日志是常干...

谈谈枚举的新用法--java(java枚举的作用与好处)

问题的由来前段时间改游戏buff功能,干了一件愚蠢的事情,那就是把枚举和运算集合在一起,然后运行一段时间后buff就出现各种问题,我当时懵逼了!事情是这样的,做过游戏的都知道,buff,需要分类型,且...

你还不懂java的日志系统吗(javaw 日志)

一、背景在java的开发中,使用最多也绕不过去的一个话题就是日志,在程序中除了业务代码外,使用最多的就是打印日志。经常听到的这样一句话就是“打个日志调试下”,没错在日常的开发、调试过程中打印日志是常干...

Java 8之后的那些新特性(三):Java System Logger

去年12月份log4j日志框架的一个漏洞,给Java整个行业造成了非常大的影响。这个事情也顺带把log4j这个日志框架推到了争议的最前线。在Java领域,log4j可能相对比较流行。而在log4j之外...

Java开发中的日志管理:让程序“开口说话”

Java开发中的日志管理:让程序“开口说话”日志是程序员的朋友,也是程序的“嘴巴”。它能让程序在运行过程中“开口说话”,告诉我们它的状态、行为以及遇到的问题。在Java开发中,良好的日志管理不仅能帮助...

吊打面试官(十二)--Java语言中ArrayList类一文全掌握

导读...

OS X 效率启动器 Alfred 详解与使用技巧

问:为什么要在Mac上使用效率启动器类应用?答:在非特殊专业用户的环境下,(每天)用户一般可以在系统中进行上百次操作,可以是点击,也可以是拖拽,但这些只是过程,而我们的真正目的是想获得结果,也就是...

Java中 高级的异常处理(java中异常处理的两种方式)

介绍异常处理是软件开发的一个关键方面,尤其是在Java中,这种语言以其稳健性和平台独立性而闻名。正确的异常处理不仅可以防止应用程序崩溃,还有助于调试并向用户提供有意义的反馈。...

【性能调优】全方位教你定位慢SQL,方法介绍下!

1.使用数据库自带工具...

全面了解mysql锁机制(InnoDB)与问题排查

MySQL/InnoDB的加锁,一直是一个常见的话题。例如,数据库如果有高并发请求,如何保证数据完整性?产生死锁问题如何排查并解决?下面是不同锁等级的区别表级锁:开销小,加锁快;不会出现死锁;锁定粒度...

看懂这篇文章,你就懂了数据库死锁产生的场景和解决方法

一、什么是死锁加锁(Locking)是数据库在并发访问时保证数据一致性和完整性的主要机制。任何事务都需要获得相应对象上的锁才能访问数据,读取数据的事务通常只需要获得读锁(共享锁),修改数据的事务需要获...