一. 异常的定义
在《java编程思想》中这样定义 异常:阻止当前方法或作用域继续执行的问题。虽然java中有异常处理机制,但是要明确一点,决不应该用"正常"的态度来看待异常。绝对一点说异常就是某种意义上的错误,就是问题,它可能会导致程序失败。之所以java要提出异常处理机制,就是要告诉开发人员,你的程序出现了不正常的情况,请注意。
记得当初学习java的时候,异常总是搞不太清楚,不知道这个异常是什么意思,为什么会有这个机制?但是随着知识的积累逐渐也对异常有一点感觉了。举一个例子来说明一下异常的用途。
public
class
Calculator {
public
int
devide(
int
num1,
int
num2) {
//判断除数是否为0
if
(num2 ==
0
) {
throw
new
IllegalArgumentException(
"除数不能为零"
);
}
return
num1/num2;
}
}
|
看一下这个类中关于除运算的方法,如果你是新手你可能会直接返回计算结果,根本不去考虑什么参数是否正确,是否合法(当然可以原谅,谁都是这样过来的)。但是我们应尽可能的考虑周全,把可能导致程序失败的"苗头"扼杀在摇篮中,所以进行参数的合法性检查就很有必要了。其中执行参数检查抛出来的那个参数非法异常,这就属于这个方法的不正常情况。正常情况下我们会正确的使用计算器,但是不排除粗心大意把除数赋值为0。如果你之前没有考虑到这种情况,并且恰巧用户数学基础不好,那么你完了。但是如果你之前考虑到了这种情况,那么很显然错误已在你的掌控之中。
二. 异常扫盲行动
今天和别人聊天时看到一个笑话:世界上最真情的相依,是你在try我在catch。无论你发神马脾气,我都默默承受,静静处理。 大多数新手对java异常的感觉就是:try...catch...。没错,这是用的最多的,也是最实用的。我的感觉就是:java异常是从"try...catch..."走来。
首先来熟悉一下java的异常体系:
Throwable 类是 Java 语言中所有错误或异常的超类(这就是一切皆可抛的东西)。它有两个子类:Error和Exception。
Error:用于指示合理的应用程序不应该试图捕获的严重问题。这种情况是很大的问题,大到你不能处理了,所以听之任之就行了,你不用管它。比如说VirtualMachineError:当 Java 虚拟机崩溃或用尽了它继续操作所需的资源时,抛出该错误。好吧,就算这个异常的存在了,那么应该何时,如何处理它呢??交给JVM吧,没有比它更专业的了。
Exception:它指出了合理的应用程序想要捕获的条件。Exception又分为两类:一种是CheckedException,一种是UncheckedException。这两种Exception的区别主要是CheckedException需要用try...catch...显示的捕获,而UncheckedException不需要捕获。通常UncheckedException又叫做RuntimeException。《effective java》指出:对于可恢复的条件使用被检查的异常(CheckedException),对于程序错误(言外之意不可恢复,大错已经酿成)使用运行时异常(RuntimeException)。
我们常见的RuntimeExcepiton有IllegalArgumentException、IllegalStateException、NullPointerException、IndexOutOfBoundsException等等。对于那些CheckedException就不胜枚举了,我们在编写程序过程中try...catch...捕捉的异常都是CheckedException。io包中的IOException及其子类,这些都是CheckedException。
三. 异常的使用
在异常的使用这一部分主要是演示代码,都是我们平常写代码的过程中会遇到的(当然只是一小部分),抛砖引玉吗!
例1. 这个例子主要通过两个方法对比来演示一下有了异常以后代码的执行流程。
public
static
void
testException1() {
int
[] ints =
new
int
[] {
1
,
2
,
3
,
4
};
System.out.println(
"异常出现前"
);
try
{
System.out.println(ints[
4
]);
System.out.println(
"我还有幸执行到吗"
);
// 发生异常以后,后面的代码不能被执行
}
catch
(IndexOutOfBoundsException e) {
System.out.println(
"数组越界错误"
);
}
System.out.println(
"异常出现后"
);
}
/*output:
异常出现前
数组越界错误
4
异常出现后
*/
|
public
static
void
testException2() {
int
[] ints =
new
int
[] {
1
,
2
,
3
,
4
};
System.out.println(
"异常出现前"
);
System.out.println(ints[
4
]);
System.out.println(
"我还有幸执行到吗"
);
// 发生异常以后,他后面的代码不能被执行
}
|
首先指出例子中的不足之处,IndexOutofBoundsException是一个非受检异常,所以不用try...catch...显示捕捉,但是我的目的是对同一个异常用不同的处理方式,看它会有什么不同的而结果(这里也就只能用它将就一下了)。异常出现时第一个方法只是跳出了try块,但是它后面的代码会照样执行的。但是第二种就不一样了直接跳出了方法,比较强硬。从第一个方法中我们看到,try...catch...是一种"事务性"的保障,它的目的是保证程序在异常的情况下运行完毕,同时它还会告知程序员程序中出错的详细信息(这种详细信息有时要依赖于程序员设计)。
例2. 重新抛出异常
public
class
Rethrow {
public
static
void
readFile(String file)
throws
FileNotFoundException {
try
{
BufferedInputStream in =
new
BufferedInputStream(
new
FileInputStream(file));
}
catch
(FileNotFoundException e) {
e.printStackTrace();
System.err.println(
"不知道如何处理该异常或者根本不想处理它,但是不做处理又不合适,这是重新抛出异常交给上一级处理"
);
//重新抛出异常
throw
e;
}
}
public
static
void
printFile(String file) {
try
{
readFile(file);
}
catch
(FileNotFoundException e) {
e.printStackTrace();
}
}
public
static
void
main(String[] args) {
printFile(
"D:/file"
);
}
}
|
异常的本意是好的,让我们试图修复程序,但是现实中我们修复的几率很小,我们很多时候就是用它来记录出错的信息。如果你厌倦了不停的处理异常,重新抛出异常对你来说可能是一个很好的解脱。原封不动的把这个异常抛给上一级,抛给调用这个方法的人,让他来费脑筋吧。这样看来,java异常(当然指的是受检异常)又给我们平添很多麻烦,尽管它的出发点是好的。
例3. 异常链的使用及异常丢失
定义三个异常类:ExceptionA,ExceptionB,ExceptionC
public
class
ExceptionA
extends
Exception {
public
ExceptionA(String str) {
super
();
}
}
public
class
ExceptionB
extends
ExceptionA {
public
ExceptionB(String str) {
super
(str);
}
}
public
class
ExceptionC
extends
ExceptionA {
public
ExceptionC(String str) {
super
(str);
}
}
|
异常丢失的情况:
public
class
NeverCaught {
static
void
f()
throws
ExceptionB{
throw
new
ExceptionB(
"exception b"
);
}
static
void
g()
throws
ExceptionC {
try
{
f();
}
catch
(ExceptionB e) {
ExceptionC c =
new
ExceptionC(
"exception a"
);
throw
c;
}
}
public
static
void
main(String[] args) {
try
{
g();
}
catch
(ExceptionC e) {
e.printStackTrace();
}
}
}
/*
exception.ExceptionC
at exception.NeverCaught.g(NeverCaught.java:12)
at exception.NeverCaught.main(NeverCaught.java:19)
*/
|
为什么只是打印出来了ExceptionC而没有打印出ExceptionB呢?这个还是自己分析一下吧!
上面的情况相当于少了一种异常,这在我们排错的过程中非常的不利。那我们遇到上面的情况应该怎么办呢?这就是异常链的用武之地:保存异常信息,在抛出另外一个异常的同时不丢失原来的异常。
public
class
NeverCaught {
static
void
f()
throws
ExceptionB{
|