1、异常简介
在Java程序运行时,可能会发生许多错误情况,导致程序停止执行。异常是由于错误或意外事件发生而在内存中生成的对象。当生成一个异常时,它需要被“抛出”,除非程序检测到异常并进行处理,否则异常就会导致应用程序停止。
要检测到抛出的异常并阻止异常终止程序,就需要创建异常处理程序。异常处理程序是在抛出异常时响应异常的代码,拦截和响应异常的过程称为异常处理。如果代码在抛出时不处理异常,则默认的异常处理程序将处理它,默认的异常处理程序会打印错误消息并导致程序崩溃。
public class BadArray
{
public static void main(String[] args)
{
// Create an array with 3 elements.
int[] numbers = { 1, 2, 3 };
// Attempt to read beyond the bounds
// of the array.
for (int i = 0; i <= 3; i++)
System.out.println(numbers[i]);
}
}
123Exception in thread "main" java.lang.ArrayIndexOutOfBoundsExceptionat BadArray.main(BadArray.java:15)
so,java异常时一个对象。异常对象是从JavaAPI中的类中创建的。该API具有广泛的异常类层次结构。
所有的异常类都来自于Error和Exception类。从Error中继承的类是指当发生严重错误时抛出的异常,例如JVM中的内部错误或内存耗尽。程序不应尝试处理这些错误,你也处理不了这些错误。我们应该处理的异常应该继承自Exception类。Exception里的子类也是更细的异常的超类。比如IOException表示于输入和输出操作相关的异常。RuntimeException表示编程错误导致的异常的超类,例如数组下标越界。
2、处理异常
如果想处理异常,我们就需要用到try语句。
try
{
(try block statements . . . ) }
catch (ExceptionType parameterName) {
(catch block statements . . . ) }
首先,关键字try出现。接下来是大括号。大括号中会有一个必需的代码块。这个代码块被称为try block。try block是正在执行的一个或多个语句,并且可能会抛出异常。你可以认为try block中的代码是“受保护的”,因为如果try block抛出异常,应用程序将不会停止。
在try block之后,将出现一个catch子句。catch语句以关键字catch开头,后面是括号,括号里是异常的类型和名字。这里实际上是一个变量声明,如果try block中的代码抛出某个异常类型的异常,则这里声明的变量将引用try中抛出的异常对象。此外,try中的语句在遇到异常的时候将会立即跳出,还会立即执行catch语句后面的代码。catch语句后面的代码称为catch block,同样需要大括号。
让我们看一个例子:
try
{
File file = new File("MyFile.txt");
Scanner inputFile = new Scanner(file);
}
catch (FileNotFoundException e)
{
System.out.println("File not found.");
}
首先,执行try block中的代码。如果此代码抛出异常,则JVM将搜索可以处理异常的catch子句。为了使catch语句能够处理异常,catch后括号中的参数必须与异常类型兼容。
此catch语句声明了一个名为e的变量作为其参数。e变量可以引用FileNotFound异常类的对象。因此,这个catch子句可以处理FileNotFound类的异常。如果try block中的代码抛出FileNotFound异常,则e变量将引用异常对象,并将执行try block中的代码。在本例中,会打印“File not found”。执行try block后,程序将继续执行try/catch语句之后出现的代码。
下图说明了出现异常及不出现异常时的状况:
在catch语句中,也可以使用多态的特性,比如接下来的例子:
try
{
number = Integer.parseInt(str);
}
catch (Exception e)
{
System.out.println("Conversion error: " +
e.getMessage());
}
虽然e实际上是一个NumberFormatException类的错误,但是所有的类都继承自Exception类,所以在这里使用Exception类来引用是完全没问题的。
3、返回默认的错误信息
每个异常类的对象都有一个getMessage方法,这个方法可以返回默认的错误信息,也就是当程序报错的时候在控制台输出的语句,看下面的例子:
/**
This program causes an error and crashes.
*/
import java.io.*; // For file I/O classes
import java.util.Scanner; // For the Scanner class
import javax.swing.JOptionPane; // For the JOptionPane class
/**
This program demonstrates how a FileNotFoundException
exception can be handled.
*/
public class ExceptionMessage
{
public static void main(String[] args)
{
File file; // For file input
Scanner inputFile; // For file input
String fileName; // To hold a file name
// Get a file name from the user.
fileName = JOptionPane.showInputDialog("Enter " +
"the name of a file:");
// Attempt to open the file.
try
{
file = new File(fileName);
inputFile = new Scanner(file);
JOptionPane.showMessageDialog(null,
"The file was found.");
}
catch (FileNotFoundException e)
{
JOptionPane.showMessageDialog(null, e.getMessage());
}
JOptionPane.showMessageDialog(null, "Done.");
System.exit(0);
}
}
如果用户输入了一个不存在的文件名,程序就会报错并返回错误信息。
4、尝试捕获多个异常
(1)如何捕捉多个异常
有的时候一个try语句会抛出多个异常(在程序执行一遍的时候,只要抓到一个异常就不会继续执行,并不会有抛出第二个异常的机会。但是在多次执行的时候,可能对于第一个对象抛出这个异常,对于第二个对象抛出那个异常,也就是说在程序复用的过程中,一个try语句两次抛出的异常可能不是一个种类),那么我们就需要用不同的catch语句来处理不同的异常。
import java.io.*; // For File class and FileNotFoundException
import java.util.*; // For Scanner and InputMismatchException
import javax.swing.JOptionPane; // For the JOptionPane class
/**
This program demonstrates how multiple exceptions can
be caught with one try statement.
*/
public class SalesReport
{
public static void main(String[] args)
{
String filename = "SalesData.txt"; // File name
int months = 0; // Month counter
double oneMonth; // One month's sales
double totalSales = 0.0; // Total sales
double averageSales; // Average sales
try
{
// Open the file.
File file = new File(filename);
Scanner inputFile = new Scanner(file);
// Process the contents of the file.
while (inputFile.hasNext())
{
// Get a month's sales amount.
oneMonth = inputFile.nextDouble();
// Accumulate the amount.
totalSales += oneMonth;
// Increment the month counter
months++;
}
// Close the file.
inputFile.close();
// Calculate the average.
averageSales = totalSales / months;
// Display the results.
JOptionPane.showMessageDialog(null,
String.format("Number of months: %d\n" +
"Total Sales: $%,.2f\n" +
"Average Sales: $%,.2f",
months, totalSales, averageSales));
}
catch(FileNotFoundException e)
{
// Thrown by the Scanner constructor when
// the file is not found.
JOptionPane.showMessageDialog(null,
"The file " + filename + " does not exist.");
}
catch(InputMismatchException e)
{
// Thrown by the Scanner class's nextDouble
// method when a non-numeric value is found.
JOptionPane.showMessageDialog(null,
"Non-numeric data found in the file.");
}
System.exit(0);
}
}
Scanner类的构造器可能抛出FileNotFoundException异常,nextDouble方法又有可能抛出InputMismatchException异常(在java.util里),所以这里的catch语句有两个用来处理这两个可能的异常。
当然我们也可以使用一个try语句来捕获多个异常,在java7中,java引入了这个新的特性。这可以减少需要捕获多个异常的尝试语句中的大量重复代码,但要缺点是只能对每个异常执行相同的操作。
try
{
(try block statements . . . ) }
catch(NumberFormatException ex)
{
respondToError();
}
catch(IOException ex)
{
respondToError();
}
对两种不同的异常,执行的操作相同,那么我们就可以把这段代码改写成这样:
try
{
(try block statements . . . ) }
catch(NumberFormatException | IOException ex)
{
respondToError();
}
请注意,在catch语句中,异常类型由一个“|”符号分隔,该符号与用于逻辑OR操作符的符号相同。你可以认为这个语句将抓住NumberFormatException或IOException。
(2)多异常的捕捉顺序
对于一个try语句中的特定的一种类型的异常,我们只能为之匹配一个catch语句,如果为一种异常写了多个语句,程序就会报错。
try
{
number = Integer.parseInt(str);
}
catch (NumberFormatException e)
{
System.out.println("Bad number format.");
}
// ERROR!!! NumberFormatException has already been caught!
catch (NumberFormatException e)
{
System.out.println(str + " is not a number.");
}
有时候,这种异常也会由多态性导致:
try
{
number = Integer.parseInt(str);
}
catch (IllegalArgumentException e)
{
System.out.println("Bad number format.");
}
// This will also cause an error.
catch (NumberFormatException e)
{
System.out.println(str + " is not a number.");
}
因为NumberFormatException是IllegalArgumentException,所以当catch抓到一个NumberFormatException异常的时候,会先匹配第一个catch。所以我们应该把更具体的异常写在考前的异常语句中。如果catch(exception e)写在第一个异常中,那么不论什么类型的异常,都会匹配到这个catch语句中,因为exception是所有异常的父类,由多态性可知,所有异常都可以是excrption类型的变量)。下面是异常类的继承关系: