📅  最后修改于: 2020-12-21 01:44:17             🧑  作者: 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类。
以下列出了最常见的已检查和未检查的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. |
一种方法使用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个元素的数组。然后代码试图访问其抛出异常阵列的第三元件。
// 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。