📜  Java-异常

📅  最后修改于: 2020-11-15 04:24:59             🧑  作者: Mango


异常(或例外事件)是在程序执行期间出现的问题。当发生异常时,程序的正常流程将中断,程序/应用程序将异常终止,因此不建议这样做,因此,应对这些异常进行处理。

出于多种不同原因可能会发生异常。以下是发生异常的一些情况。

  • 用户输入了无效的数据。

  • 找不到需要打开的文件。

  • 通信过程中网络连接丢失,或者JVM内存不足。

这些异常中的某些是由用户错误引起的,其他是由程序员错误引起的,而其他则是由以某种方式失败的物理资源引起的。

基于这些,我们分为三类。您需要了解它们,才能了解Java中异常处理的工作原理。

  • 检查的异常-检查的异常是编译器在编译时检查(通知)的异常,这些也称为编译时异常。这些异常不能简单地被忽略,程序员应该照顾(处理)这些异常。

例如,如果在程序中使用FileReader类从文件中读取数据,如果在其构造函数中指定的文件不存在,则会发生FileNotFoundException ,并且编译器会提示程序员处理该异常。

import java.io.File;
import java.io.FileReader;

public class FilenotFound_Demo {

   public static void main(String args[]) {        
      File file = new File("E://file.txt");
      FileReader fr = new FileReader(file); 
   }
}

如果您尝试编译上述程序,则会出现以下异常。

输出

C:\>javac FilenotFound_Demo.java
FilenotFound_Demo.java:8: error: unreported exception FileNotFoundException; must be caught or declared to be thrown
      FileReader fr = new FileReader(file);
                      ^
1 error

–由于FileReader类的read()close()方法会引发IOException,因此可以观察到编译器与FileNotFoundException一起通知处理IOException。

  • 未检查的异常-未检查的异常是在执行时发生的异常。这些也称为运行时异常。其中包括编程错误,例如逻辑错误或API使用不当。编译时将忽略运行时异常。

例如,如果您在程序中声明了大小为5的数组,并尝试调用该数组的6元素,则会发生ArrayIndexOutOfBoundsExceptionexception

public class Unchecked_Demo {
   
   public static void main(String args[]) {
      int num[] = {1, 2, 3, 4};
      System.out.println(num[5]);
   }
}

如果编译并执行上述程序,则会出现以下异常。

输出

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
    at Exceptions.Unchecked_Demo.main(Unchecked_Demo.java:8)
  • 错误-这些根本不是例外,而是由用户或程序员无法控制的问题。错误通常会在代码中被忽略,因为您几乎无法对错误做任何事情。例如,如果发生堆栈溢出,则会出现错误。在编译时也将忽略它们。

异常层次

所有异常类都是java.lang.Exception类的子类型。异常类是Throwable类的子类。除了异常类之外,还有另一个名为Error的子类,它是从Throwable类派生的。

错误是发生严重故障时发生的异常情况,这些错误不会由Java程序处理。生成错误以指示运行时环境生成的错误。示例:JVM内存不足。通常,程序无法从错误中恢复。

Exception类具有两个主要的子类:IOException类和RuntimeException类。

例外1

以下列出了最常见的已检查和未检查的Java内置异常

例外方法

以下是Throwable类中可用的重要方法的列表。

Sr.No. Method & Description
1

public String getMessage()

Returns a detailed message about the exception that has occurred. This message is initialized in the Throwable constructor.

2

public Throwable getCause()

Returns the cause of the exception as represented by a Throwable object.

3

public String toString()

Returns the name of the class concatenated with the result of getMessage().

4

public void printStackTrace()

Prints the result of toString() along with the stack trace to System.err, the error output stream.

5

public StackTraceElement [] getStackTrace()

Returns an array containing each element on the stack trace. The element at index 0 represents the top of the call stack, and the last element in the array represents the method at the bottom of the call stack.

6

public Throwable fillInStackTrace()

Fills the stack trace of this Throwable object with the current stack trace, adding to any previous information in the stack trace.

捕捉异常

一种方法使用trycatch关键字的组合来捕获异常。在可能产生异常的代码周围放置了一个try / catch块。 try / catch块中的代码称为受保护的代码,使用try / catch的语法如下所示-

句法

try {
   // Protected code
} catch (ExceptionName e1) {
   // Catch block
}

容易出现异常的代码放在try块中。发生异常时,该异常发生由与其关联的catch块处理。每个try块都应紧随其后的是catch块或finally块。

catch语句涉及声明您要捕获的异常类型。如果受保护的代码中发生异常,则将检查try之后的catch块。如果在catch块中列出了发生的异常类型,则将异常传递到catch块的方式与将参数传递给方法参数的方式一样。

以下是声明了2个元素的数组。然后代码试图访问其抛出异常阵列的第三元件。

// File Name : ExcepTest.java
import java.io.*;

public class ExcepTest {

   public static void main(String args[]) {
      try {
         int a[] = new int[2];
         System.out.println("Access element three :" + a[3]);
      } catch (ArrayIndexOutOfBoundsException e) {
         System.out.println("Exception thrown  :" + e);
      }
      System.out.println("Out of the block");
   }
}

这将产生以下结果-

输出

Exception thrown  :java.lang.ArrayIndexOutOfBoundsException: 3
Out of the block

多个捕获块

一个try块之后可以是多个catch块。多个catch块的语法如下所示-

句法

try {
   // Protected code
} catch (ExceptionType1 e1) {
   // Catch block
} catch (ExceptionType2 e2) {
   // Catch block
} catch (ExceptionType3 e3) {
   // Catch block
}

前面的语句演示了三个catch块,但是一次尝试后可以有任意多个。如果受保护的代码中发生异常,则将该异常引发到列表中的第一个catch块。如果抛出的异常的数据类型与ExceptionType1相匹配,它将在此处被捕获。如果不是,则异常传递到第二个catch语句。这种情况一直持续到捕获到异常或通过所有捕获捕获异常为止,在这种情况下,当前方法停止执行,并且异常被抛出给调用堆栈中的上一个方法。

这是代码段,显示了如何使用多个try / catch语句。

try {
   file = new FileInputStream(fileName);
   x = (byte) file.read();
} catch (IOException i) {
   i.printStackTrace();
   return -1;
} catch (FileNotFoundException f) // Not valid! {
   f.printStackTrace();
   return -1;
}

捕获多种类型的异常

从Java 7开始,您可以使用单个catch块处理多个异常,因此此功能简化了代码。这是你将如何做-

catch (IOException|FileNotFoundException ex) {
   logger.log(ex);
   throw ex;

掷/掷关键字

如果方法不处理检查的异常,则该方法必须使用throws关键字对其进行声明。 throws关键字出现在方法签名的末尾。

您可以使用throw关键字引发异常,可以是新实例化的异常,也可以是刚刚捕获的异常。

尝试了解throw和throw关键字之间的区别, throws用于推迟对已检查异常的处理, throw用于显式调用异常。

以下方法声明它抛出RemoteException-

import java.io.*;
public class className {

   public void deposit(double amount) throws RemoteException {
      // Method implementation
      throw new RemoteException();
   }
   // Remainder of class definition
}

一个方法可以声明它抛出多个异常,在这种情况下,这些异常在用逗号分隔的列表中声明。例如,以下方法声明它抛出RemoteException和InsufficientFundsException-

import java.io.*;
public class className {

   public void withdraw(double amount) throws RemoteException, 
      InsufficientFundsException {
      // Method implementation
   }
   // Remainder of class definition
}

最后一块

finally块位于try块或catch块之后。无论是否发生异常,始终都会执行一个finally代码块。

通过使用finally块,无论受保护的代码发生什么情况,您都可以运行要执行的任何清理类型的语句。

最后一个块出现在catch块的末尾,语法如下:

句法

try {
   // Protected code
} catch (ExceptionType1 e1) {
   // Catch block
} catch (ExceptionType2 e2) {
   // Catch block
} catch (ExceptionType3 e3) {
   // Catch block
}finally {
   // The finally block always executes.
}

public class ExcepTest {

   public static void main(String args[]) {
      int a[] = new int[2];
      try {
         System.out.println("Access element three :" + a[3]);
      } catch (ArrayIndexOutOfBoundsException e) {
         System.out.println("Exception thrown  :" + e);
      }finally {
         a[0] = 6;
         System.out.println("First element value: " + a[0]);
         System.out.println("The finally statement is executed");
      }
   }
}

这将产生以下结果-

输出

Exception thrown  :java.lang.ArrayIndexOutOfBoundsException: 3
First element value: 6
The finally statement is executed

请注意以下内容-

  • 没有try语句,catch子句将不存在。

  • 每当存在try / catch块时,都不必具有finally子句。

  • 没有catch子句或finally子句,try块将不存在。

  • 在try,catch和finally块之间不能存在任何代码。

尝试资源

通常,当我们使用任何资源(例如流,连接等)时,必须使用finally块显式关闭它们。在以下程序中,我们使用FileReader从文件中读取数据,并使用finally块将其关闭。

import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class ReadData_Demo {

   public static void main(String args[]) {
      FileReader fr = null;        
      try {
         File file = new File("file.txt");
         fr = new FileReader(file); char [] a = new char[50];
         fr.read(a);   // reads the content to the array
         for(char c : a)
         System.out.print(c);   // prints the characters one by one
      } catch (IOException e) {
         e.printStackTrace();
      }finally {
         try {
            fr.close();
         } catch (IOException ex) {        
            ex.printStackTrace();
         }
      }
   }
}

try-with-resources ,也称为自动资源管理,是Java 7中引入的一种新的异常处理机制,它会自动关闭try catch块中使用的资源。

要使用此语句,您只需要在括号内声明所需的资源,并且所创建的资源将在代码块末尾自动关闭。以下是try-with-resources语句的语法。

句法

try(FileReader fr = new FileReader("file path")) {
   // use the resource
   } catch () {
      // body of catch 
   }
}

以下是使用try-with-resources语句读取文件中数据的程序。

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

public class Try_withDemo {

   public static void main(String args[]) {
      try(FileReader fr = new FileReader("E://file.txt")) {
         char [] a = new char[50];
         fr.read(a);   // reads the contentto the array
         for(char c : a)
         System.out.print(c);   // prints the characters one by one
      } catch (IOException e) {
         e.printStackTrace();
      }
   }
}

在使用try-with-resources语句时,请牢记以下几点。

  • 要使用带有try-with-resources语句的类,它应该实现AutoCloseable接口,并且它的close()方法在运行时自动被调用。

  • 您可以在try-with-resources语句中声明多个类。

  • 在try-with-resources语句的try块中声明多个类时,这些类以相反的顺序关闭。

  • 除了括号内的资源声明外,其他所有内容均与try块的正常try / catch块相同。

  • 在try块开始之前,实例化在try中声明的资源。

  • 在try块中声明的资源被隐式声明为final。

用户定义的异常

您可以使用Java创建自己的异常。编写自己的异常类时,请记住以下几点:

  • 所有例外必须是Throwable的子级。

  • 如果要编写由Handle或Delare Rule自动执行的检查异常,则需要扩展Exception类。

  • 如果要编写运行时异常,则需要扩展RuntimeException类。

我们可以如下定义自己的Exception类-

class MyException extends Exception {
}

您只需要扩展预定义的Exception类即可创建自己的Exception。这些被视为检查异常。以下InsufficientFundsException类是用户定义的异常,它扩展了Exception类,使其成为受检查的异常。异常类与其他任何类一样,包含有用的字段和方法。

// File Name InsufficientFundsException.java
import java.io.*;

public class InsufficientFundsException extends Exception {
   private double amount;
   
   public InsufficientFundsException(double amount) {
      this.amount = amount;
   }
   
   public double getAmount() {
      return amount;
   }
}

为了演示使用我们的用户定义的异常,下面的CheckingAccount类包含一个抛出InsufficientFundsException的withdraw()方法。

// File Name CheckingAccount.java
import java.io.*;

public class CheckingAccount {
   private double balance;
   private int number;
   
   public CheckingAccount(int number) {
      this.number = number;
   }
   
   public void deposit(double amount) {
      balance += amount;
   }
   
   public void withdraw(double amount) throws InsufficientFundsException {
      if(amount <= balance) {
         balance -= amount;
      }else {
         double needs = amount - balance;
         throw new InsufficientFundsException(needs);
      }
   }
   
   public double getBalance() {
      return balance;
   }
   
   public int getNumber() {
      return number;
   }
}

下面的BankDemo程序演示了如何调用CheckingAccount的deposit()和withdraw()方法。

// File Name BankDemo.java
public class BankDemo {

   public static void main(String [] args) {
      CheckingAccount c = new CheckingAccount(101);
      System.out.println("Depositing $500...");
      c.deposit(500.00);
      
      try {
         System.out.println("\nWithdrawing $100...");
         c.withdraw(100.00);
         System.out.println("\nWithdrawing $600...");
         c.withdraw(600.00);
      } catch (InsufficientFundsException e) {
         System.out.println("Sorry, but you are short $" + e.getAmount());
         e.printStackTrace();
      }
   }
}

编译上述三个文件,然后运行BankDemo。这将产生以下结果-

输出

Depositing $500...

Withdrawing $100...

Withdrawing $600...
Sorry, but you are short $200.0
InsufficientFundsException
         at CheckingAccount.withdraw(CheckingAccount.java:25)
         at BankDemo.main(BankDemo.java:13)

常见例外

在Java中,可以定义异常和错误的两个类别。

  • JVM异常-这些是JVM专有或逻辑抛出的异常/错误。示例:NullPointerException,ArrayIndexOutOfBoundsException,ClassCastException。

  • 程序异常-这些异常由应用程序或API程序员显式抛出。示例:IllegalArgumentException,IllegalStateException。