异常(Exceptions)
异常(或异常事件)是在执行程序期间出现的问题。 当发生Exception时,程序的正常流程被中断并且程序/应用程序异常终止,这是不推荐的,因此,要处理这些异常。
出于许多不同的原因可能会发生异常。 以下是发生异常的一些情况。
用户输入了无效数据。
找不到需要打开的文件。
在通信过程中丢失了网络连接,或者JVM内存不足。
其中一些异常是由用户错误引起的,其他异常是由程序员错误引起的,而其他异常是由以某种方式失败的物理资源引起的。
基于这些,我们有三类例外。 您需要了解它们以了解异常处理在Java中的工作原理。
Checked exceptions - 已检查的异常是编译器在编译时检查(通知)的异常,这些异常也称为编译时异常。 这些异常不能简单地忽略,程序员应该处理(处理)这些异常。
例如,如果在程序中使用FileReader类从文件中读取数据,如果其构造函数中指定的文件不存在,则会发生FileNotFoundException ,编译器会提示程序员处理异常。
例子 (Example)
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);
}
}
如果您尝试编译上述程序,则会出现以下异常。
输出 (Output)
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
Note - 由于FileReader类的read()和close()方法抛出IOException,您可以观察到编译器通知处理IOException以及FileNotFoundException。
Unchecked exceptions - 未经检查的异常是在执行时发生的异常。 这些也称为Runtime Exceptions 。 这些包括编程错误,例如逻辑错误或API的不当使用。 编译时忽略运行时异常。
例如,如果您在程序中声明了一个大小为5的数组,并且尝试调用该数组的第 6 个元素,则会发生ArrayIndexOutOfBoundsExceptionexception 。
例子 (Example)
public class Unchecked_Demo {
public static void main(String args[]) {
int num[] = {1, 2, 3, 4};
System.out.println(num[5]);
}
}
如果编译并执行上述程序,则会出现以下异常。
输出 (Output)
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
at Exceptions.Unchecked_Demo.main(Unchecked_Demo.java:8)
Errors - 这些根本不是例外,而是超出用户或程序员控制的问题。 代码中通常会忽略错误,因为您很少对错误做任何事情。 例如,如果发生堆栈溢出,则会出现错误。 它们在编译时也被忽略。
异常层次结构
所有异常类都是java.lang.Exception类的子类型。 异常类是Throwable类的子类。 除了异常类之外,还有另一个名为Error的子类,它派生自Throwable类。
错误是在严重故障的情况下发生的异常情况,Java程序不处理这些情况。 生成错误以指示运行时环境生成的错误。 示例:JVM内存不足。 通常,程序无法从错误中恢复。
Exception类有两个主要的子类:IOException类和RuntimeException类。
以下列出了最常见的已检查和未检查的Java内置异常 。
例外方法
以下是Throwable类中可用的重要方法列表。
Sr.No. | 方法和描述 |
---|---|
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的语法如下所示 -
语法 (Syntax)
try {
// Protected code
} catch (ExceptionName e1) {
// Catch block
}
容易出现异常的代码放在try块中。 发生异常时,发生的异常由与其关联的catch块处理。 每个try块应该紧跟一个catch块或者最后一个块。
catch语句涉及声明您尝试捕获的异常类型。 如果受保护代码中发生异常,则会检查try之后的catch块(或多个块)。 如果发生的异常类型列在catch块中,则异常将传递给catch块,就像将参数传递给方法参数一样。
例子 (Example)
以下是使用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");
}
}
这将产生以下结果 -
输出 (Output)
Exception thrown :java.lang.ArrayIndexOutOfBoundsException: 3
Out of the block
多个捕获块
try块后面可以跟多个catch块。 多个catch块的语法如下所示 -
语法 (Syntax)
try {
// Protected code
} catch (ExceptionType1 e1) {
// Catch block
} catch (ExceptionType2 e2) {
// Catch block
} catch (ExceptionType3 e3) {
// Catch block
}
之前的语句演示了三个catch块,但只需一次尝试即可获得任意数量的块。 如果受保护代码中发生异常,则会将异常抛出到列表中的第一个catch块。 如果抛出的异常的数据类型与ExceptionType1匹配,则会在那里捕获它。 如果不是,则异常传递给第二个catch语句。 这种情况一直持续到异常被捕获或落入所有捕获,在这种情况下,当前方法停止执行,异常将被抛到调用堆栈上的前一个方法。
例子 (Example)
以下是显示如何使用多个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关键字抛出异常,可以是新实例化的异常,也可以是刚捕获的异常。
尝试理解throws和throw关键字之间的区别, throws用于推迟处理已检查的异常, throw用于显式调用异常。
以下方法声明它抛出RemoteException -
例子 (Example)
import java.io.*;
public class className {
public void deposit(double amount) throws RemoteException {
// Method implementation
throw new RemoteException();
}
// Remainder of class definition
}
方法可以声明它抛出多个异常,在这种情况下,异常在用逗号分隔的列表中声明。 例如,以下方法声明它抛出RemoteException和InsufficientFundsException -
例子 (Example)
import java.io.*;
public class className {
public void withdraw(double amount) throws RemoteException,
InsufficientFundsException {
// Method implementation
}
// Remainder of class definition
}
最后一块
finally块遵循try块或catch块。 无论发生异常,最终都会执行最后一段代码。
使用finally块允许您运行要执行的任何清理类型语句,无论受保护代码中发生什么。
最后一个块出现在catch块的末尾,并具有以下语法 -
语法 (Syntax)
try {
// Protected code
} catch (ExceptionType1 e1) {
// Catch block
} catch (ExceptionType2 e2) {
// Catch block
} catch (ExceptionType3 e3) {
// Catch block
}finally {
// The finally block always executes.
}
例子 (Example)
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");
}
}
}
这将产生以下结果 -
输出 (Output)
Exception thrown :java.lang.ArrayIndexOutOfBoundsException: 3
First element value: 6
The finally statement is executed
请注意以下事项 -
没有try语句就不能存在catch子句。
只要存在try/catch块,就不必强制终止子句。
如果没有catch子句或finally子句,则try块不能出现。
try,catch,finally块之间不能出现任何代码。
The try-with-resources
通常,当我们使用流,连接等任何资源时,我们必须使用finally块显式关闭它们。 在下面的程序中,我们使用FileReader从文件中读取数据,然后使用finally块关闭它。
例子 (Example)
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 ,也称为automatic resource management ,是Java 7中引入的一种新的异常处理机制,它自动关闭try catch块中使用的资源。
要使用此语句,只需在括号内声明所需的资源,创建的资源将在块结束时自动关闭。 以下是try-with-resources语句的语法。
语法 (Syntax)
try(FileReader fr = new FileReader("file path")) {
// use the resource
} catch () {
// body of catch
}
}
以下是使用try-with-resources语句读取文件中数据的程序。
例子 (Example)
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-block开始之前实例化。
在try块声明的资源被隐式声明为final。
User-defined Exceptions
您可以在Java中创建自己的异常。 在编写自己的异常类时,请牢记以下几点 -
所有例外必须是Throwable的孩子。
如果要编写由Handle或Declare Rule自动强制执行的已检查异常,则需要扩展Exception类。
如果要编写运行时异常,则需要扩展RuntimeException类。
我们可以定义我们自己的Exception类,如下所示 -
class MyException extends Exception {
}
您只需要扩展预定义的Exception类来创建自己的Exception。 这些被视为已检查的例外情况。 以下InsufficientFundsException类是一个用户定义的异常,它扩展了Exception类,使其成为一个已检查的异常。 异常类与任何其他类一样,包含有用的字段和方法。
例子 (Example)
// 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。 这将产生以下结果 -
输出 (Output)
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 Exceptions - 这些是JVM Exceptions独占或逻辑抛出的异常/错误。 示例:NullPointerException,ArrayIndexOutOfBoundsException,ClassCastException。
Programmatic Exceptions - 应用程序或API程序员明确抛出这些异常。 示例:IllegalArgumentException,IllegalStateException。