Java 异常

Java 异常

异常(或异常事件)是程序执行过程中出现的问题。当一个异常发生时,程序的正常流程被打断,程序/应用程序异常终止,这是不推荐的,因此必须处理这些异常。

异常可以出现有许多不同的原因。以下是一些异常发生的情况。

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

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

  • 网络连接在通信过程中断开或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,您可以观察到编译器提醒处理IOException,以及FileNotFoundException。

  • Unchecked exceptions − 不受检查异常是在执行时发生的异常。也被称为 运行时异常 。这些包括编程错误,如逻辑错误或不正确地使用API。运行时异常在编译时被忽略。

例如,如果您在程序中声明了一个大小为5的数组,并尝试调用数组的第6个元素,则会抛出ArrayIndexOutOfBoundsException异常。

示例

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类的子类。除了异常类,还有一个从Throwable类派生的子类叫做Error。

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

异常类有两个主要的子类:IOException类和RuntimeException类。

Java 异常

以下是一个最常见的已检查和未检查的 Java内置的异常 的列表。

异常方法

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

序号 方法与描述
1 public String getMessage() - 返回关于发生异常的详细消息。该消息在Throwable构造函数中初始化。
2 public Throwable getCause() - 返回由Throwable对象表示的异常的原因。
3 public String toString() - 返回类名与getMessage()结果的连接。
4 public void printStackTrace() - 将toString()的结果与堆栈跟踪打印到System.err,即错误输出流。
5 public StackTraceElement [] getStackTrace() - 返回包含堆栈跟踪上每个元素的数组。索引0处的元素表示调用堆栈的顶部,数组中的最后一个元素表示调用堆栈底部的方法。
6 public Throwable fillInStackTrace() - 填充此Throwable对象的堆栈跟踪与当前堆栈跟踪,添加到堆栈跟踪中的任何先前信息。

捕获异常

一个方法使用try和catch关键字的组合来捕获异常。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个元素的数组。然后,代码尝试访问该数组的第3个元素,它会引发异常。

// 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

多个Catch块

try块可以跟随多个catch块。多个catch块的语法如下所示−

语法

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

前面的语句展示了三个catch块,但在一个try后面可以有任意数量的catch块。如果在受保护的代码中发生异常,异常会被抛到列表中的第一个catch块。如果被抛出的异常的数据类型与ExceptionType1匹配,它就会在那里被捕获。如果不匹配,异常会继续传递到第二个catch语句。这个过程会一直持续,直到异常被捕获或者穿过所有的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;

抛出/throw关键字

如果一个方法没有处理已检查异常,那么该方法必须使用 throws 关键字来声明。throws关键字出现在方法签名的末尾。

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

试着理解throws和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代码块

finally代码块跟在try代码块或catch代码块之后。不管是否出现异常,finally代码块中的代码总会执行。

使用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块之间不能存在任何代码。

使用try-with-resources

通常,当我们使用任何资源例如流、连接等时,必须使用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/catch块的try块相同。

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

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

用户定义的异常

您可以在Java中创建自己的异常。编写自定义异常类时,请记住以下几点−

  • 所有异常都必须是Throwable的子类。

  • 如果要编写一个由Handle或Declare规则自动强制执行的受检异常,则需要扩展Exception类。

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

我们可以如下定义我们自己的异常类−

class MyException extends 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类包含一个withdraw()方法,它会抛出InsufficientFundsException异常。

// 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("\nWithdrawing100...");
         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...

Withdrawing100...

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

常见异常

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

  • JVM异常 - 这些是仅由JVM抛出的异常/错误。例如:NullPointerException(空指针异常),ArrayIndexOutOfBoundsException(数组下标越界异常),ClassCastException(类转换异常)。

  • 编程异常 - 这些异常由应用程序或API编程人员显式抛出。例如:IllegalArgumentException(非法参数异常),IllegalStateException(非法状态异常)。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程