认识Java异常,一文读懂Java异常处理知识文集。

发布时间:2023年12月26日

在这里插入图片描述

🏆作者简介,普修罗双战士,一直追求不断学习和成长,在技术的道路上持续探索和实践。
🏆多年互联网行业从业经验,历任核心研发工程师,项目技术负责人。
🎉欢迎 👍点赞?评论?收藏

🔎 一、Java常见异常知识文集(1)

🍁🍁 01. Error 和 Exception 区别是什么?

在这里插入图片描述

在编程中,Error 和 Exception 是两个不同的概念,它们分别代表着不同的问题类型。

  1. Error:

    Error 通常指的是严重的编译时错误或系统级错误,通常是程序无法处理的问题,它们发生在程序运行之前或者运行时期间,例如内存不足、硬件故障、操作系统问题等。一般情况下,Error 无法通过代码来捕获和处理,因为它们通常表示着严重的问题,程序无法继续正常运行。一般情况下程序员无法通过代码来恢复或者解决这类问题。

  2. Exception:

    Exception 是指在程序运行过程中,出现了一些意外的情况,例如除零错误、空指针引用、数组索引越界等。它们通常是由于程序本身的逻辑问题,需要通过代码来捕获和处理。在 Java 和许多其他编程语言中,异常是以对象的形式存在的,程序员可以通过 try-catch 块来捕获异常,并在 catch 块中处理异常,从而使程序能够继续正常运行,或者给用户提供友好的错误提示。

因此,Error 表示严重的系统级错误,无法通过代码来处理;而 Exception 表示程序运行过程中的意外情况,需要通过代码来捕获和处理。在实际编程中,程序员需要根据具体的情况来选择如何处理 Error 和 Exception。

🍁🍁 02. 运行时异常和一般异常(受检异常)区别是什么?

在这里插入图片描述

在Java中,异常可以分为两类:运行时异常(Runtime Exception)和非运行时异常(Checked Exception)。

  1. 运行时异常:

    运行时异常是指继承自 RuntimeException 的异常。它们在程序执行期间才会被触发,不需要显式地捕获或声明。运行时异常通常是由程序的逻辑错误造成的,例如空指针引用、除零错误、数组索引越界等。

    它们在程序运行过程中表示了一种不正常的情况,但是由于是由程序逻辑问题引起的,程序员可以通过改进代码逻辑来避免这些异常的发生。运行时异常的特点是,它们会导致程序的中断和终止,且不需要在代码中显示捕获和处理。

  2. 非运行时异常:

    非运行时异常是指除了继承自 RuntimeException 的异常之外的所有异常。它们在程序编写期间就已经被定义和声明,必须在代码中显式地捕获和处理,否则编译器会报错。

    非运行时异常通常是由外部环境或资源的问题引起的,例如输入输出异常(IOException)、数据库异常(SQLException)等。这些异常通常是可以预见的,程序员需要在代码中加入异常处理机制,以保证程序能够正常处理这些异常情况。

因此,运行时异常和非运行时异常的区别在于:

  • 运行时异常是由程序逻辑问题引起的,通常可以通过改进代码来避免;而非运行时异常通常是由外部环境或资源问题引起的,需要在代码中显式处理。
  • 运行时异常不需要在代码中显式捕获和处理,程序会终止;而非运行时异常必须在代码中显式捕获和处理,否则编译器会报错。
  • 运行时异常是由程序执行期间触发的,非运行时异常在编译期间就已经定义和声明。
  • 运行时异常是 RuntimeException 的子类,非运行时异常不是 RuntimeException 的子类。

🍁🍁 03. JVM 是如何处理异常的?

在这里插入图片描述

JVM(Java Virtual Machine)在处理异常时遵循一套规定的异常处理机制。下面是 JVM 处理异常的基本流程:

  1. JVM 检测异常

    当异常发生时,无论是运行时异常还是非运行时异常,JVM 都会检测到异常的发生。这可以是由于代码中遇到了 throw 语句,或者是由于程序执行过程中出现了错误的操作导致的异常。

  2. 异常抛出

    在检测到异常后,JVM 会创建一个表示异常的对象。如果异常是通过 throw 语句抛出的,那么 JVM 将使用 throw 语句中指定的对象创建异常对象。如果异常是由于错误的操作或其他系统级问题引起的,JVM 将根据异常类型,在内部创建一个相应类型的异常对象。

  3. 异常处理机制:

    JVM 会尝试在当前执行栈中查找能够处理该异常的异常处理器(Exception Handler)。如果找到了合适的异常处理器,JVM 将将控制流转移到处理器代码的位置,并执行处理器中的相应逻辑来处理异常。

  4. 异常处理器的查找路径:

    JVM 会按照方法调用栈的顺序查找能够处理异常的异常处理器。从报告异常的方法开始,逐级向上查找方法的调用者,直到找到一个能够处理该类型异常的异常处理器。

  5. 异常处理:

    一旦找到了合适的异常处理器,它将根据异常类型执行相应的处理逻辑。处理逻辑可以包括捕获异常、处理异常、记录日志、抛出新的异常等。如果异常被处理了,程序将继续执行异常处理器之后的代码。如果没有找到合适的异常处理器,程序将终止,并打印异常信息。

总结来说,JVM 在处理异常时会检测异常的发生,创建异常对象,查找能够处理异常的异常处理器,并执行相应的处理逻辑。异常处理机制保证了异常的及时处理,提高了程序的健壮性和可靠性。

🍁🍁 04. final、finally、finalize 有什么区别?

在这里插入图片描述

final、finally、finalize 是 Java 中三个具有不同含义和用途的关键字。它们的区别如下所示:

  1. final:

    final 是一个修饰符,可以用于修饰类、变量和方法。使用 final 修饰的类不能被继承,使用 final 修饰的变量表示常量,一旦赋值后不可修改,使用 final 修饰的方法不能被子类进行重写。以下是示例:

    final class FinalClass { // final 修饰类,不能被继承
        // ...
    }
    
    public class Example {
        final int MAX_VALUE = 10; // final 修饰变量,表示常量
        // ...
    
        final void printMessage() { // final 修饰方法,不能被重写
            // ...
        }
    }
    
  2. finally:

    finally 是一个关键字,用于与 trycatch 结合使用,用于定义在异常处理结束后一定会执行的代码块,不论是否抛出异常。通常在 finally 代码块中释放资源、关闭连接等情况下使用。以下是示例:

    try {
        // 可能抛出异常的代码
    } catch (Exception e) {
        // 异常处理逻辑
    } finally {
        // 无论是否发生异常均会执行的代码
    }
    
  3. finalize:

    finalize 是一个方法,定义在类中,由垃圾回收器在对象被销毁之前调用。它属于 Java 中的垃圾回收机制的一部分。可以重写 finalize 方法来定义对象在被销毁之前需要进行的清理工作。以下是示例:

    public class Example {
        // ...
    
        @Override
        protected void finalize() throws Throwable {
            // 执行对象的清理工作
    
            super.finalize();
        }
    }
    

需要注意的是,随着 Java 的发展,很少使用 finalize 方法进行对象清理。推荐使用其他方式(例如 try-with-resources)来确保资源的释放和清理,以提高代码的可靠性和性能。

🍁🍁 05. finally块中的代码什么时候会执行?

在这里插入图片描述

finally 块中的代码在以下情况下会执行:

  1. 异常发生时:

    当 try 块中的代码发生异常时,无论是否捕获到该异常,finally 块中的代码都会执行。它提供了一种机制,确保无论是否发生异常,某些代码逻辑都会执行完毕后再终止。

    try {
        // 可能抛出异常的代码
    } catch (Exception e) {
        // 异常处理逻辑
    } finally {
        // 无论是否发生异常均会执行的代码
    }
    
  2. 异常未发生时:

    即使 try 块中的代码没有发生异常,finally 块中的代码也会执行。这样可以保证在 try 块中执行完业务逻辑后,最终资源的释放或清理操作都会被执行。

    try {
        // 可能抛出异常的代码
    } finally {
        // 无论是否发生异常均会执行的代码
    }
    

    以下是一个示例,演示了 finally 块中的代码在异常发生时和异常未发生时的执行情况:

    public class Example {
        public static void main(String[] args) {
            try {
                int result = divide(10, 0);
                System.out.println("结果:" + result);
            } catch (ArithmeticException e) {
                System.out.println("发生异常:" + e.getMessage());
            } finally {
                System.out.println("finally 块中的代码");
            }
        }
    
        public static int divide(int a, int b) {
            return a / b;
        }
    }
    

    输出结果如下:

    发生异常:/ by zero
    finally 块中的代码
    

可以看到,在发生异常时,catch 块中的代码执行了异常处理逻辑,然后 finally 块中的代码也执行了。即使异常被捕获和处理,finally 块仍然会执行。

🍁🍁 06. throw 和 throws 的区别是什么?

在这里插入图片描述

throw 和 throws 是 Java 中用于处理异常的关键字,它们的作用和用法有所不同:

  1. throw:

    throw 关键字用于手动抛出一个异常对象,可以将一个异常抛出到方法外部,然后由调用者或上层代码进行处理。通常用于在代码中显式地抛出异常,以便对异常进行定制化处理或传播异常信息。

    示例:

    public void processInput(int value) {
        if (value < 0) {
            throw new IllegalArgumentException("输入值不能为负数");
        }
        // 其他业务逻辑
    }
    
  2. throws:

    throws 关键字用于在方法声明中标识该方法可能抛出的异常类型,以便通知调用者需要处理这些可能抛出的异常。在方法签名中使用 throws 关键字,可以让调用者知道需要进行异常处理,可以选择捕获处理异常或者继续向上抛出。

    示例:

    public void readFile(String fileName) throws FileNotFoundException {
        // 读取文件的业务逻辑
    }
    

总结来说,throw 用于手动抛出异常对象,而 throws 用于在方法声明中标识可能抛出的异常类型。在使用中,throw 通常用于具体的异常抛出点,而 throws 通常用于方法定义中声明可能抛出的异常类型。

🍁🍁 07. throws关键字的作用是什么?它和try-catch有什么区别?

throws 关键字用于在方法声明中标识该方法可能抛出的异常类型,它的作用是通知调用者需要处理这些可能抛出的异常。通过在方法签名中使用 throws 关键字,可以让调用者知道需要进行异常处理,可以选择捕获处理异常或者继续向上抛出。

主要作用包括:

  1. 声明可能抛出的异常类型:

    使用 throws 关键字可以明确告诉方法的调用者,方法可能抛出的异常类型。这个信息对于代码的组织和调用者的异常处理是很有帮助的。

  2. 分离异常处理责任:

    使用 throws 关键字可以将方法内可能发生的异常向上抛出,将异常处理的责任交给调用者,实现了异常处理的分离。这样使得代码的可维护性和可读性更好,尤其是对于复杂的代码逻辑。

而与之相比,try-catch 用于在代码块中捕获和处理异常,有以下主要区别:

  1. 异常处理方式不同:

    throws 是将异常抛给上层调用者处理,而 try-catch 是在当前的代码块中进行捕获和处理异常。

  2. 对异常的处理粒度不同:

    throws 是在方法级别进行异常处理,将可能的异常抛到方法调用者处理,而 try-catch 是在代码块级别进行异常处理,将代码块中可能抛出的异常进行捕获和处理。

  3. 对异常的影响范围不同:

    throws 将异常抛给上层调用者后,可能会继续传播给更上层的调用者,形成异常处理链。而 try-catch 则是在当前代码块内处理异常,不会继续向上层传播。

总之,throws 关键字用于声明方法可能抛出的异常,让调用者知道需要进行异常处理,而 try-catch 用于在当前代码块中捕获和处理异常。它们在异常处理的方式、异常处理粒度以及对异常的影响范围等方面有所不同。

下面用表格来展示二者的区别,更简洁明了:

区别throwstry-catch
用途在方法声明中标识可能抛出的异常类型在代码块中捕获和处理异常
异常处理方式将异常抛给上层调用者处理在当前的代码块中进行捕获和处理异常
异常处理粒度方法级别代码块级别
异常影响范围异常可能继续传播给上层调用者异常不会继续向上层传播,局部处理异常

🍁🍁 08. try-catch-finally 中哪个部分可以省略?

在 Java 中,try-catch-finally 结构中的 catch 和 finally 部分都可以省略,但 try 是必需的。

  1. 省略 catch 部分:

    如果不需要对特定类型的异常进行捕获和处理,可以省略 catch 部分。这样,如果 try 块中发生异常,异常会被抛出到调用栈的上一级,由上层的异常处理机制来处理异常。

    示例:

    try {
        // 可能发生异常的代码
    } finally {
        // 清理资源等操作
    }
    
  2. 省略 finally 部分:

    如果不需要在异常发生或未发生时执行特定的清理或收尾操作,可以省略 finally 部分。

    示例:

    try {
        // 可能发生异常的代码
    } catch (Exception e) {
        // 捕获特定类型异常并处理
    }
    

总之,try 是必需的,因为它标识了需要进行异常检测的代码块,而 catch 和 finally 部分可以根据实际情况进行省略。

🍁🍁 09. try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?

在 Java 中,如果在 try-catch-finally 结构中,catch 块中使用了 return 语句,finally 块依然会执行。无论 catch 块中是否有 return 语句,finally 块都会在 try 或 catch 块执行后被执行。

下面是一个示例来说明这一点:

public class FinallyExample {
    public static void main(String[] args) {
        System.out.println(testMethod());    
    }
    
    public static int testMethod() {
        try {
            System.out.println("Inside try block");
            int result = 10 / 0; // 触发ArithmeticException
            return 1;
        } catch (ArithmeticException e) {
            System.out.println("Inside catch block");
            return 2; // 在 catch 中使用 return
        } finally {
            System.out.println("Inside finally block");
        }
    }
}

在上面的示例中,try 块中的代码会触发 ArithmeticException,然后会进入 catch 块,执行 catch 块中的 return 语句返回值 2,最终会执行 finally 块中的代码。因此,无论 catch 块中是否有 return 语句,finally 块都会被执行。

在这个示例中,程序输出会是:

Inside try block
Inside catch block
Inside finally block
2

因此,无论 catch 中是否有 return 语句,finally 块都会被执行。

🍁🍁 10. try-catch-finally块的作用是什么?它们之间的执行顺序是怎样的?

try-catch-finally 块是一种异常处理机制,用于捕获和处理可能发生的异常,并在必要时进行清理操作。它们的执行顺序如下:

  1. 程序首先执行 try 块中的代码。如果在 try 块中发生了异常,那么 try 块之后的代码将不再执行,而是直接跳转到与抛出异常类型匹配的 catch 块。

  2. 如果匹配到了相应的 catch 块,将执行该 catch 块中的代码,用于捕获和处理异常。catch 块可以处理特定类型的异常,也可以使用通用的 Exception 类型来捕获所有异常。catch 块之后的代码将被执行。

  3. 无论是否出现异常,无论是在 try 块中还是在 catch 块中,finally 块中的代码总是会被执行。finally 块中通常用于进行资源清理、关闭连接等操作。如果在 catch 块中使用了 return 语句,在 finally 块执行完成后,return 的结果将被返回。

下面是一个示例来说明执行顺序:

public class FinallyExample {
    public static void main(String[] args) {
        System.out.println(testMethod());
    }
    
    public static int testMethod() {
        try {
            System.out.println("Inside try block");
            int result = 10 / 0; // 触发ArithmeticException
            return 1;
        } catch (ArithmeticException e) {
            System.out.println("Inside catch block");
            return 2;
        } finally {
            System.out.println("Inside finally block");
        }
    }
}

在上面的示例中,try 块中的代码会触发 ArithmeticException,然后会进入 catch 块,执行 catch 块中的 return 语句返回值 2,最终会执行 finally 块中的代码。因此,程序输出如下:

Inside try block
Inside catch block
Inside finally block
2

总结来说,try-catch-finally 块的作用是用于捕获和处理异常,并提供一个清理操作的机会。它们的执行顺序是先执行 try 块中的代码,如果有异常则进入对应的 catch 块,最后无论是否出现异常,都会执行 finally 块中的代码。

🍁🍁 11. 常见的 RuntimeException 有哪些?

在这里插入图片描述

常见的 RuntimeException 包括但不限于以下几种:

  1. NullPointerException(空指针异常):当应用程序试图在对 null 对象的引用上调用实例方法、访问或修改其实例变量时,抛出该异常。

  2. ArrayIndexOutOfBoundsException(数组下标越界异常):当应用程序试图访问数组的元素超出了有效范围时,抛出该异常。

  3. IllegalArgumentException(非法参数异常):当应用程序的方法接收到不合法或不适当的参数时,抛出该异常。

  4. IllegalStateException(非法状态异常):当对象的状态不适合执行某个操作时,抛出该异常。例如,在调用对象的某个方法之前没有正确初始化或上下文状态错误。

  5. ArithmeticException(算术异常):当出现除零或溢出等算术错误时,抛出该异常。

  6. ClassCastException(类转换异常):当试图将对象强制转换为其不是有效或无关的子类型时,抛出该异常。

  7. UnsupportedOperationException(不支持的操作异常):当调用不支持的操作时,抛出该异常。通常在某些类中声明的方法不支持特定的操作时会抛出该异常。

  8. NumberFormatException(数字格式异常):当将字符串转换为数字类型时,但字符串的格式不符合数字格式时,抛出该异常。

上述只是一些常见的 RuntimeException,还有其他的 RuntimeException 子类异常。当程序运行过程中出现这些异常时,它们通常表示代码逻辑错误或错误的使用方式。

🍁🍁 12. Java常见异常有哪些?举例说明。

在这里插入图片描述

Java 中常见的异常可以分为两类:已检查异常(Checked Exception)和未检查异常(Unchecked Exception)。

  1. 已检查异常(Checked Exception):
  • IOException(输入输出异常):在文件操作、网络通信等过程中可能抛出。

  • SQLException(SQL 异常):在数据库操作过程中可能抛出。

  • ClassNotFoundException(类未找到异常):当试图加载类但无法找到对应的类文件时抛出。

  • InterruptedException(线程中断异常):在多线程编程中,当一个线程等待另一个线程时被中断,抛出该异常。

  1. 未检查异常(Unchecked Exception):
  • NullPointerException(空指针异常):当试图在 null 对象上调用实例方法时抛出。

  • ArrayIndexOutOfBoundsException(数组下标越界异常):当试图访问数组越界时抛出。

  • IllegalArgumentException(非法参数异常):当传递给方法的参数不合法时抛出。

  • ArithmeticException(算术异常):当进行算术运算错误时抛出。

  • ClassCastException(类转换异常):当试图将对象强制转换为不相关的类型时抛出。

这些只是 Java 中常见的异常,实际上还有许多其他异常,每种异常都对应不同的错误或异常情况。

🍁🍁 13. Java中的异常分为哪几种类型?它们之间有什么区别?

在这里插入图片描述

在Java中,异常可以分为三种类型:Error、Checked Exception(已检查异常)和Unchecked Exception(未检查异常)。它们之间的区别主要在于处理方式和产生原因。

  1. Error:

    Error 通常指示程序本身的错误,这些错误通常是严重的,无法通过代码来恢复或处理。通常发生在 Java 虚拟机自身的问题,如内存不足、系统崩溃等,程序无法捕获和处理 Error。例如:OutOfMemoryError、StackOverflowError等。

  2. Checked Exception(已检查异常):

    Checked Exception 是指在编译期间需要进行处理的异常。它们通常要求在代码中显式地进行捕获和处理,否则编译会报错。这些异常通常是外部因素导致的问题,可能不可避免,需要通过程序来处理。例如:IOException、SQLException等。

  3. Unchecked Exception(未检查异常):

    Unchecked Exception 通常是由程序本身的错误或逻辑错误引起的,它们属于RuntimeException及其子类,可以不进行显式的捕获和处理。这类异常在编译时不会强制要求处理,通常是由程序员的错误引起的,例如空指针异常、数组越界异常等。

区别:

  • Error 是严重的问题,通常是指示虚拟机本身的错误,程序无法捕获和处理。
  • Checked Exception 需要在编译时进行处理,强制要求程序员进行合适的处理。
  • Unchecked Exception 通常是由程序逻辑错误引起的,不要求在编译时显式处理,但仍需要在代码中进行适当的处理。

🍁🍁 14. Error和Exception之间有什么区别?

在这里插入图片描述

Error 和 Exception 是 Java 中两个不同的类层级,它们之间的区别主要在于它们的作用和处理方式。

  1. Error:
  • Error 是 Throwable 类的子类,表示严重的问题,通常指示虚拟机本身的错误或系统级问题。

  • Error 通常是不可恢复的,表示虚拟机或底层系统发生了无法或难以恢复的错误,例如内存不足(OutOfMemoryError)、栈溢出(StackOverflowError)等。

  • Error 类型的异常不应该被捕获和处理,因为它们通常表示无法继续正常执行程序。

  1. Exception:
  • Exception 是 Throwable 类的子类,表示运行时出现的可预测的异常情况。

  • Exception 分为两类:已检查异常(Checked Exception)和未检查异常(Unchecked Exception)。

  • 已检查异常(Checked Exception)在编译时要求显式地捕获和处理,通常是由外部因素引起的,需要通过程序进行处理,以确保程序的正确性。

  • 未检查异常(Unchecked Exception)通常是由程序错误或逻辑错误引起的,不要求在编译时强制处理,但仍需要在代码中进行适当的处理,以避免程序的不可预测行为。

总结:

  • Error 表示严重的问题,通常是虚拟机或系统级别的错误,不应该被捕获和处理。
  • Exception 表示程序运行过程中的异常情况,分为已检查异常和未检查异常,应根据具体情况进行捕获和处理。

以下是一个表格,说明 Error 和 Exception 之间的区别:

ErrorException
类型表示严重的问题,系统级错误表示可恢复的异常情况
处理方式不应被捕获和处理需要适当地捕获和处理
示例OutOfMemoryError、StackOverflowErrorIOException、NullPointerException

这个表格更能够清晰地比较并说明 Error 和 Exception 之间的差异。

🍁🍁 15. RuntimeException和Checked Exception有什么区别?

RuntimeException 是 Exception 的子类,它们之间的区别主要在于它们的检查和处理方式。

  1. Checked Exception(已检查异常):
  • Checked Exception 是 Exception 的一种子类,但不包括 RuntimeException 及其子类。

  • Checked Exception 必须在编译时显式地捕获和处理,否则会导致编译错误。

  • Checked Exception 是由外部因素引起的,需要通过程序进行处理,以确保程序的正确性。例如:IOException、SQLException 等。

  1. RuntimeException(未检查异常):
  • RuntimeException 是 Exception 的一种子类,包括运行时错误和逻辑错误,通常由程序员的错误行为引起。

  • RuntimeException 及其子类不要求在编译时显式处理,可以选择捕获和处理,但不强制要求。

  • RuntimeException 表示程序本身的错误或逻辑错误,因此应该通过代码的质量控制和良好的设计来避免它们的发生。例如:NullPointerException、ArrayIndexOutOfBoundsException 等。

总结:

  • Checked Exception 要求在编译时显式捕获和处理,通常由外部因素引起,需要通过程序来处理。
  • RuntimeException 及其子类不要求在编译时显式处理,通常由程序员的错误行为引起,应通过代码的质量控制和良好的设计来避免它们的发生。

以下是一个表格,说明 RuntimeException 和 Checked Exception 之间的区别:

区别Checked ExceptionRuntimeException
检查时间编译时运行时
异常处理强制要求捕获和处理不要求显式捕获和处理,可选择处理
异常类型由外部因素引起,需要处理通常由程序员错误行为或逻辑错误引起
示例IOException、SQLException 等NullPointerException、ArrayIndexOutOfBoundsException 等

这个表格更能够清晰地比较并说明 RuntimeException 和 Checked Exception 之间的差异。

🍁🍁 16. NoClassDefFoundError 和 ClassNotFoundException 区别?

NoClassDefFoundError 和 ClassNotFoundException 是 Java 中的两种不同类型的错误,它们的区别如下:

  1. NoClassDefFoundError(类定义未找到错误):
  • NoClassDefFoundError 表示虚拟机或类加载器在运行时尝试加载某个类的定义时未能找到该类的定义。

  • 这种错误通常发生在编译阶段已经存在的类,但在运行时无法找到所需的类定义,可能是因为类路径问题、类文件丢失、名称错写等原因引起。

  1. ClassNotFoundException(类未找到异常):
  • ClassNotFoundException 是在代码中使用 Class.forName()、ClassLoader.loadClass() 等方法时,无法找到对应的类而触发的异常。

  • 这种异常通常发生在运行时,表示在类加载的过程中未找到指定的类。

总结:

  • NoClassDefFoundError 是指在编译时存在的类在运行时未能被找到,可能是由于类路径配置错误或者类文件丢失所致。
  • ClassNotFoundException 是在运行时加载类时未找到对应的类,可能是由于类名拼写错误或者类确实不存在所致。

因此,虽然两者都涉及类加载问题,但错误的触发时机和原因略有不同,需要针对具体情况进行调查和处理。

以下是一个表格,说明 NoClassDefFoundError 和 ClassNotFoundException 之间的区别:

NoClassDefFoundErrorClassNotFoundException
错误类型类定义未找到错误类未找到异常
引发时机运行时运行时
类的状态编译时存在的类代码中要使用但实际不存在的类
错误发生原因类路径问题、类文件丢失、名称错写等类名拼写错误、类确实不存在等
具体触发情况在虚拟机或类加载器尝试加载类定义时未找到该类定义在将类加载到内存时,无法找到对应的类定义
示例在运行时尝试实例化一个对象时未找到对应类在使用 Class.forName() 加载类时未找到对应的类

这个表格能够清晰地比较并说明 NoClassDefFoundError 和 ClassNotFoundException 之间的差异。

🍁🍁 17. NullPointerException是什么异常?它在什么情况下会被抛出?

NullPointerException(空指针异常)是 Java 中常见的运行时异常。它表示在程序中使用了一个空对象的引用(null),而对该引用进行了操作。当程序尝试对空对象进行方法调用、属性访问或数组访问时,就会抛出 NullPointerException 异常。

NullPointerException 主要在以下情况下会被抛出:

  1. 对象引用为 null:当试图通过空对象引用来访问其方法或属性时,会触发 NullPointerException。例如:

    String str = null;
    int length = str.length(); // 抛出 NullPointerException
    
  2. 数组引用为 null:当试图通过空数组引用来访问数组元素时,会触发 NullPointerException。例如:

    int[] arr = null;
    int element = arr[0]; // 抛出 NullPointerException
    
  3. 方法返回 null:当一个方法返回 null,而调用方在使用该方法的返回值时未进行空值检查,也可能触发 NullPointerException。例如:

    public String getName() {
        return null;
    }
    
    String name = getName();
    int length = name.length(); // 抛出 NullPointerException
    

为了避免 NullPointerException,我们应该在使用对象引用之前进行空值检查,或者在必要的情况下使用条件语句来处理可能为空的情况。

🍁🍁 18. 如何处理多个异常的情况?可以有多个catch块吗?

处理多个异常的情况可以通过多个 catch 块来实现,每个 catch 块用于捕获不同类型的异常,并提供针对各种异常类型的处理逻辑。以下是一个简单的示例:

try {
    // 可能会抛出各种异常的代码
    // ...
} catch (IOException e) {
    // 处理I/O异常的逻辑
} catch (SQLException e) {
    // 处理数据库异常的逻辑
} catch (NumberFormatException e) {
    // 处理数字格式异常的逻辑
} catch (Exception e) {
    // 处理其他未知异常的逻辑
}

通过上述代码,可以看到针对不同类型的异常分别使用了不同的 catch 块来进行处理。

异常链(Exception Chaining)指的是在捕获异常后,将捕获的异常作为参数传递给新的异常,形成一种异常信息的传递和包装机制。异常链的作用是保留原始异常的信息,同时提供更高层次的异常信息以便于排查问题。

异常链的典型应用是在处理异常时,将底层的异常信息添加到顶层异常中,以提供更加完整的异常信息。在 Java 中,Exception 类的构造函数可以接受一个原始异常作为参数,从而形成异常链。

可以有多个 catch 块,每个 catch 块用于捕获不同类型的异常,并提供相应的处理逻辑。在 Java 中,多个 catch 块按照顺序匹配异常,一旦某个 catch 块捕获到了对应的异常类型,就会执行该 catch 块中的代码,并不会再向下匹配其他 catch 块。因此,如果有多个 catch 块,应该将范围较大的异常类型放在后面的 catch 块中,以免遮蔽更具体的异常类型。

🍁🍁 19. 什么是异常链(Exception Chaining)?它的作用是什么?

异常链(Exception Chaining)是一种异常处理的机制,它将一个异常信息传递给另一个异常,形成异常信息的传递和包装结构。通常情况下,异常链用于在捕获异常后,将原始异常信息保存并传递给更高层次的异常。

异常链的作用包括:

  1. 保留原始异常信息:异常链允许将底层的异常信息传递到更高层的异常中,从而保留了原始异常的信息。这有助于开发人员在排查问题时更好地理解异常产生的根本原因。

  2. 提供更加完整的异常信息:异常链可以帮助开发人员分析异常的发生路径,从顶层异常一直追溯到底层异常,有助于提供更完整的异常信息以便进行故障诊断和修复。

在Java中,异常链通常是通过在捕获异常后,将原始异常作为参数传递给新的异常来建立的。例如,通过在新异常的构造函数中传递原始异常对象,从而形成异常链。这样,即使在异常传递的过程中,原始异常信息也不会丢失,而能够一直保留在异常链中。

🍁🍁 20. 如何自定义一个异常类?举例说明?

在这里插入图片描述

自定义异常类是指根据特定业务需求或逻辑条件创建的用于表示特定异常情况的自定义异常类型。通过自定义异常类,开发人员可以更好地管理和区分不同类型的异常情况,并为这些异常情况提供专门的处理逻辑。

要自定义一个异常类,可以创建一个新的类并让它继承自 Java 内置的 Exception 或它的子类。通常情况下,自定义异常类应该提供多个构造函数以满足不同的使用场景,并且可以添加一些自定义的字段和方法来进一步描述和处理异常情况。

以下是一个简单的自定义异常类的示例,假设我们要创建一个名为CustomException的自定义异常类:

public class CustomException extends Exception {
    private int errorCode;

    public CustomException() {
        super();
    }

    public CustomException(String message) {
        super(message);
    }

    public CustomException(String message, int errorCode) {
        super(message);
        this.errorCode = errorCode;
    }

    public int getErrorCode() {
        return errorCode;
    }
}

在这个示例中,CustomException 继承自 Exception 类,它包含了一个 errorCode 字段用于描述异常的错误代码,并提供了三个构造函数:一个无参构造函数、一个带有 message 参数的构造函数和一个同时带有 message 和 errorCode 参数的构造函数。通过这些构造函数,可以灵活地在不同情况下创建 CustomException 对象,并提供异常的相关信息。

使用自定义异常类的示例代码如下:

public class CustomExceptionExample {
    public static void main(String[] args) {
        try {
            throw new CustomException("Custom exception message", 500);
        } catch (CustomException e) {
            System.out.println("CustomException caught: " + e.getMessage() + ", errorCode: " + e.getErrorCode());
        }
    }
}

通过自定义异常类,开发人员可以根据具体的业务需求和异常情况创建自定义的异常类型,并在程序中使用它来表示特定的异常条件,并提供相应的处理逻辑。

在这里插入图片描述

文章来源:https://blog.csdn.net/m0_50308467/article/details/135166584
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。