Java 教程

Java 控制语句

面向对象编程

Java 内置类

Java 文件处理

Java 错误与异常

Java 多线程

Java 同步

Java 网络编程

Java 集合

Java 接口

Java 数据结构

Java 集合算法

高级 Java

Java 杂项

Java API 和框架

Java 类参考

Java 有用资源

Java - 异常



什么是 Java 中的异常?

异常(或异常事件)是在程序执行期间出现的问题。当异常发生时,程序的正常流程被打断,程序/应用程序异常终止,这是不推荐的,因此,这些异常需要被处理。

为什么发生异常?

异常可能由于多种原因发生。以下是发生异常的一些场景。

  • 用户输入了无效数据。

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

  • 网络连接在通信过程中断开或 JVM 内存不足。

其中一些异常是由用户错误引起的,另一些是由程序员错误引起的,还有一些是由以某种方式发生故障的物理资源引起的。

Java 异常类别

基于此,我们有以下几类异常。您需要了解它们才能知道 Java 中的异常处理是如何工作的。

  • 受检异常
  • 非受检异常
  • 错误

Java 受检异常

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

示例: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。

Java 非受检异常

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

示例:Java 中的非受检异常

例如,如果您在程序中声明了一个大小为 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 错误

这些根本不是异常,而是超出用户或程序员控制范围的问题。在您的代码中通常会忽略错误,因为您很少能对错误做任何事情。例如,如果发生堆栈溢出,则会发生错误。它们在编译时也被忽略。

Java 异常层次结构

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

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

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

Exceptions1

以下是Java内置异常中最常见的一些已检查异常和未检查异常的列表。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对象的堆栈跟踪,并添加到堆栈跟踪中的任何先前信息。

捕获异常:Java中的异常处理

方法使用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个元素的数组。然后代码尝试访问数组的第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;

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

请注意以下几点:

  • catch子句不能独立存在,必须与try语句一起使用。

  • 在存在try/catch块时,不一定需要finally子句。

  • try块不能独立存在,必须与catch子句或finally子句一起使用。

  • 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块的普通try/catch块相同。

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

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

Java中的用户定义异常

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

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

  • 如果您想编写由Handle or Declare Rule自动执行的已检查异常,则需要扩展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类包含一个抛出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异常

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

  • JVM异常 - 这些是JVM专门或逻辑上抛出的异常/错误。例如:NullPointerException、ArrayIndexOutOfBoundsException、ClassCastException。

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

广告
© . All rights reserved.