📜  Java中的异常

📅  最后修改于: 2022-05-13 01:54:48.210000             🧑  作者: Mango

Java中的异常

Java中的异常处理是处理运行时错误的有效手段之一,可以保持应用程序的正常流程。 Java异常处理是一种处理运行时错误的机制,例如 ClassNotFoundException、IOException、SQLException、RemoteException 等。

异常是在程序执行期间(即在运行时)发生的不希望或意外事件,它会破坏程序指令的正常流程。程序可以捕获和处理异常。当方法中发生异常时,它会创建一个对象。该对象称为异常对象。它包含有关异常的信息,例如异常的名称和描述以及异常发生时程序的状态。

发生异常的主要原因

  • 用户输入无效
  • 设备故障
  • 网络连接丢失
  • 物理限制(磁盘内存不足)
  • 代码错误
  • 打开不可用的文件

错误代表不可恢复的情况,例如Java虚拟机(JVM)内存不足、内存泄漏、堆栈溢出错误、库不兼容、无限递归等。错误通常超出程序员的控制范围,我们不应该尝试处理错误。

让我们讨论最重要的部分,即错误和异常之间的区别,如下所示:

  • 错误:错误表示合理的应用程序不应尝试捕获的严重问题。
  • 异常:异常表示合理的应用程序可能试图捕获的条件。

异常层次结构

所有异常和错误类型都是Throwable类的子类,后者是层次结构的基类。一个分支由Exception领导。此类用于用户程序应捕获的异常情况。 NullPointerException 是此类异常的一个示例。 Java运行时系统 (JVM) 使用另一个分支Error来指示与运行时环境本身 (JRE) 相关的错误。 StackOverflowError 就是此类错误的一个示例。

Java中的异常层次结构

例外类型

Java定义了与其各种类库相关的几种异常类型。 Java还允许用户定义自己的异常。

例外类型

可以通过两种方式对异常进行分类:

  1. 内置异常
    • 检查异常
    • 未经检查的异常
  2. 用户定义的异常

让我们讨论上面定义的列出的异常,如下所示:

A. 内置异常:

内置异常是Java库中可用的异常。这些异常适用于解释某些错误情况。

  • 检查异常:已检查异常称为编译时异常,因为编译器在编译时检查这些异常。
  • 未检查的异常:未检查的异常与已检查的异常正好相反。编译器不会在编译时检查这些异常。简单来说,如果程序抛出未经检查的异常,即使我们没有处理或声明它,程序也不会给出编译错误。

B. 用户定义的异常:

有时, Java中的内置异常无法描述某种情况。在这种情况下,用户还可以创建称为“用户定义的异常”的异常。

Java中异常处理的优点如下:

  1. 提供完成程序执行
  2. 轻松识别程序代码和错误处理代码
  3. 错误的传播
  4. 有意义的错误报告
  5. 识别错误类型

JVM 如何处理异常?

默认异常处理:只要在方法内部发生异常,该方法就会创建一个称为异常对象的对象并将其交给运行时系统(JVM)。异常对象包含异常的名称和描述以及发生异常的程序的当前状态。创建异常对象并在运行时系统中处理它称为抛出异常。可能有一个方法列表,这些方法已被调用以获取发生异常的方法。这个有序的方法列表称为Call Stack 。现在将发生以下过程。

  • 运行时系统搜索调用堆栈以找到包含可以处理发生的异常的代码块的方法。该代码块称为异常处理程序
  • 运行时系统从发生异常的方法开始搜索,并以调用方法的相反顺序在调用堆栈中进行。
  • 如果它找到合适的处理程序,则将发生的异常传递给它。适当的处理程序意味着抛出的异常对象的类型与它可以处理的异常对象的类型相匹配。
  • 如果运行时系统搜索调用堆栈上的所有方法并且找不到合适的处理程序,则运行时系统将异常对象移交给默认异常处理程序,这是运行时系统的一部分。该处理程序以下列格式打印异常信息并异常终止程序。
Exception in thread "xxx" Name of Exception : Description
... ...... ..  // Call Stack

查看下图以了解调用堆栈的流程。

调用栈

插图:

Java
// Java Program to Demonstrate How Exception Is Thrown
 
// Class
// ThrowsExecp
class GFG {
 
    // Main driver method
    public static void main(String args[])
    {
        // Taking an empty string
        String str = null;
        // Getting length of a string
        System.out.println(str.length());
    }
}


Java
// Java Program to Demonstrate Exception is Thrown
// How the runTime System Searches Call-Stack
// to Find Appropriate Exception Handler
 
// Class
// ExceptionThrown
class GFG {
 
    // Method 1
    // It throws the Exception(ArithmeticException).
    // Appropriate Exception handler is not found
    // within this method.
    static int divideByZero(int a, int b)
    {
 
        // this statement will cause ArithmeticException
        // (/by zero)
        int i = a / b;
 
        return i;
    }
 
    // The runTime System searches the appropriate
    // Exception handler in method also but couldn't have
    // found. So looking forward on the call stack
    static int computeDivision(int a, int b)
    {
 
        int res = 0;
 
        // Try block to check for exceptions
        try {
 
            res = divideByZero(a, b);
        }
 
        // Catch block to handle NumberFormatException
        // exception Doesn't matches with
        // ArithmeticException
        catch (NumberFormatException ex) {
            // Display message when exception occurs
            System.out.println(
                "NumberFormatException is occurred");
        }
        return res;
    }
 
    // Method 2
    // Found appropriate Exception handler.
    // i.e. matching catch block.
    public static void main(String args[])
    {
 
        int a = 1;
        int b = 0;
 
        // Try block to check for exceptions
        try {
            int i = computeDivision(a, b);
        }
 
        // Catch block to handle ArithmeticException
        // exceptions
        catch (ArithmeticException ex) {
 
            // getMessage() will print description
            // of exception(here / by zero)
            System.out.println(ex.getMessage());
        }
    }
}


Java
// Java Program to Demonstrate
// Need of try-catch Clause
 
// Class
class GFG {
 
    // Main driver method
    public static void main(String[] args)
    {
        // Taking an array of size 4
        int[] arr = new int[4];
 
        // Now this statement will cause an exception
        int i = arr[4];
 
        // This statement will never execute
        // as above we caught with an exception
        System.out.println("Hi, I want to execute");
    }
}


输出:

让我们看一个示例,说明运行时系统如何在调用堆栈上搜索适当的异常处理代码。

例子:

Java

// Java Program to Demonstrate Exception is Thrown
// How the runTime System Searches Call-Stack
// to Find Appropriate Exception Handler
 
// Class
// ExceptionThrown
class GFG {
 
    // Method 1
    // It throws the Exception(ArithmeticException).
    // Appropriate Exception handler is not found
    // within this method.
    static int divideByZero(int a, int b)
    {
 
        // this statement will cause ArithmeticException
        // (/by zero)
        int i = a / b;
 
        return i;
    }
 
    // The runTime System searches the appropriate
    // Exception handler in method also but couldn't have
    // found. So looking forward on the call stack
    static int computeDivision(int a, int b)
    {
 
        int res = 0;
 
        // Try block to check for exceptions
        try {
 
            res = divideByZero(a, b);
        }
 
        // Catch block to handle NumberFormatException
        // exception Doesn't matches with
        // ArithmeticException
        catch (NumberFormatException ex) {
            // Display message when exception occurs
            System.out.println(
                "NumberFormatException is occurred");
        }
        return res;
    }
 
    // Method 2
    // Found appropriate Exception handler.
    // i.e. matching catch block.
    public static void main(String args[])
    {
 
        int a = 1;
        int b = 0;
 
        // Try block to check for exceptions
        try {
            int i = computeDivision(a, b);
        }
 
        // Catch block to handle ArithmeticException
        // exceptions
        catch (ArithmeticException ex) {
 
            // getMessage() will print description
            // of exception(here / by zero)
            System.out.println(ex.getMessage());
        }
    }
}
输出
/ by zero

程序员如何处理异常?

自定义异常处理: Java异常处理通过五个关键字进行管理: trycatchthrowthrowsfinally 。简而言之,这是它们的工作方式。您认为会引发异常的程序语句包含在 try 块中。如果在 try 块中发生异常,则会抛出该异常。您的代码可以捕获此异常(使用 catch 块)并以某种合理的方式处理它。 Java运行时系统会自动抛出系统生成的异常。要手动抛出异常,请使用关键字 throw。从方法中抛出的任何异常都必须由 throws 子句指定。在 try 块完成后绝对必须执行的任何代码都放在 finally 块中。

需要 try-catch 子句(自定义异常处理)

考虑下面的程序,以便更好地理解 try-catch 子句。

例子:

Java

// Java Program to Demonstrate
// Need of try-catch Clause
 
// Class
class GFG {
 
    // Main driver method
    public static void main(String[] args)
    {
        // Taking an array of size 4
        int[] arr = new int[4];
 
        // Now this statement will cause an exception
        int i = arr[4];
 
        // This statement will never execute
        // as above we caught with an exception
        System.out.println("Hi, I want to execute");
    }
}

输出:

输出说明:在上面的示例中,数组定义为大小,即您只能访问索引 0 到 3 的元素。但是您尝试访问索引 4 处的元素(错误地),这就是它抛出异常的原因。在这种情况下,JVM 会异常终止程序。语句 System.out.println(“嗨,我要执行”);永远不会执行。要执行它,我们必须使用 try-catch 处理异常。因此,要继续程序的正常流程,我们需要一个 try-catch 子句。

如何使用 try-catch 子句?

try {
    // block of code to monitor for errors
    // the code you think can raise an exception
} catch (ExceptionType1 exOb) {
    // exception handler for ExceptionType1
} catch (ExceptionType2 exOb) {
    // exception handler for ExceptionType2
}
// optional
finally {  // block of code to be executed after try block ends 
}

需要记住以下几个关键点:  

  • 在一个方法中,可能有多个语句可能引发异常,因此将所有这些语句放在它们自己的try块中,并在它们自己的catch块中为它们中的每一个提供一个单独的异常处理程序。
  • 如果try块内发生异常,则该异常由与其关联的异常处理程序处理。要关联异常处理程序,我们必须在其后放置一个catch块。可以有多个异常处理程序。每个catch块都是一个异常处理程序,用于处理由其参数指示的类型的异常。参数 ExceptionType 声明它可以处理的异常类型,并且必须是从Throwable类继承的类的名称。
  • 对于每个 try 块,可以有零个或多个 catch 块,但只有一个final 块。
  • finally 块是可选的。无论 try 块中是否发生异常,它都会被执行。如果发生异常,那么它将在try 和 catch 块之后执行。如果没有发生异常,那么它将在try块之后执行。 Java中的finally块用于放置重要代码,例如清理代码,例如关闭文件或关闭连接。

该摘要通过以下视觉辅助进行描述,如下所示:  

相关文章:

  • Java中的异常类型
  • 已检查与未检查的异常
  • Java中的投掷