1.异常基础
(1)异常分类
Throwable 类是 Java 语言中所有错误或异常的超类(这就是一切皆可抛的东西)。它有两个子类:Error和Exception。
Error类体系描述了Java运行系统中的内部错误以及资源耗尽的情形。应用程序不应该抛出这种类型的对象(一般是由虚拟机抛出)。如果出现这种错误,除了尽力使程序安全
退出外,在其他方面是无能为力的。所以,在进行程序设计时,应该更关注Exception体系
Exception:它指出了合理的应用程序想要捕获的条件。Exception又分为两类:一种是CheckedException,一种是UncheckedException。
这两种Exception的区别主要是CheckedException需要用try...catch...显示的捕获,而UncheckedException不需要捕获。通常
UncheckedException又叫做RuntimeException。对于可恢复的条件使用被检查的异常(CheckedException),对于程序错误(言外之意不
可恢复,大错已经酿成)使用运行时异常(RuntimeException)。
RuntimeException:RuntimeException体系包括错误的类型转换、数组越界访问和试图访问空指针等等。
其他非RuntimeException(IOException等等):这类异常一般是外部错误,例如试图从文件尾后读取数据等,这并不是程序本身的错误,而是在应用环境中出现的外部错误。
在编写程序过程中try...catch...捕捉的异常都是CheckedException。io包中的IOException及其子类,这些都是CheckedException。
1. java.lang.NullPointerException
"程序遇上了空指针",就是调用了未经初始化的对象或者是不具有的对象。
2.java.lang.ArithmeticException
"数学运算异常",比如程序中出现了除以零这样的运算就会出这样的异常,对这种异常,大家就要好好检查一下本人程序中涉及到数学运算的地方,公式是不是有不妥了。
3.java.lang.ArrayIndexOutOfBoundsException
"数组下标越界".
4.java.lang.IllegalAccessException
这个异常的注释是"没有访问权限",当应用程序要调用一个类,但当前的方法即没有对该类的访问权限便会出现这个异常。对程序中用了package的情况下要注意这个异常。
try...catch...是一种"事务性"的保障,它的目的是保证程序在异常的情况下运行完毕,同时它还会告知程序员程序中出错的详细信息。
下面是这几个类的层次图:
java.lang.Object
java.lang.Throwable
java.lang.Exception
java.lang.RuntimeException
java.lang.Error
java.lang.ThreadDeath
(2)异常的例子
第一个例子:
public class Calculator {
public int devide(int num1, int num2) {
//判断除数是否为0
if(num2 == 0) {
throw new IllegalArgumentException("除数不能为零");
}
return num1/num2;
}
}
第二个例子:
/*利用关键字throws进行异常处理*/
public class ExceptionTest1 {
public static int divide(int iNum1, int iNum2) throws ArithmeticException {
return iNum1 / iNum2;
}
public static void main(String[] args) {
try {
int iNum = divide(5,0);
System.out.println(iNum);
} catch (ArithmeticException e) {
e.printStackTrace();//指出异常的类型、性质、栈层次及出现程序中的位置
}
}
}
异常对象中包含的信息 :一般情况下,异常对象唯一有用的信息就是类型信息。但使用异常带字符串的构造函数时,这个字符串还可以作为额外的信息。调用异常对象的
getMessage()、toString()或者printStackTrace()方法可以分别得到异常对象的额外信息、类名和调用堆栈的信息。并且后一种包含的信息是前一种的超集。
catch语句可以有多个,用来匹配多个异常,匹配上多个中一个后,执行catch语句块时候仅仅执行匹配上的异常。catch的类型是Java语言中定义的或者程序员自己定义的,表示代码抛
出异常的类型,异常的变量名表示抛出异常的对象的引用,如果catch捕获并匹配上了该异常,那么就可以直接用这个异常变量名,此时该异常变量名指向所匹配的异常,并且在catch代
码块中可以直接引用。
(3)重新抛出异常
重新抛出异常对你来说可能是一个很好的解脱。原封不动的把这个异常抛给上一级,抛给调用这个方法的人,让他来费脑筋吧:
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;
}
}
(4)异常链
Throwable 包含了其线程创建时线程执行堆栈的快照。它还包含了给出有关错误更多信息的消息字符串,可以包含 cause(原因):另一个导致此 throwable 抛出的 throwable。
它也称为异常链设施,因为 cause 自身也会有 cause,依此类推,就形成了异常链,每个异常都是由另一个异常引起的。
异常链就是把原始的异常包装为新的异常类,并在新的异常类中封装了原始异常类,这样做的目的在于找到异常的根本原因。
通过Throwable的两个构造方法可以创建自定义的包含异常原因的异常类型:
Throwable(String message, Throwable cause)
构造一个带指定详细消息和 cause 的新 throwable。
Throwable(Throwable cause)
构造一个带指定 cause 和 (cause==null ? null :cause.toString())(它通常包含类和 cause 的详细消息)的详细消息的新 throwable。
getCause()
返回此 throwable 的 cause;如果 cause 不存在或未知,则返回 null。
initCause(Throwable cause)
将此 throwable 的 cause 初始化为指定值。
在Throwable的子类Exception中,也有类似的指定异常原因的构造方法:
Exception(String message, Throwable cause)
构造带指定详细消息和原因的新异常。
Exception(Throwable cause)
根据指定的原因和 (cause==null ? null : cause.toString()) 的详细消息构造新异常(它通常包含 cause 的类和详细消息)。
因此,可以通过扩展Exception类来构造带有异常原因的新的异常类。
异常链的特性是所有异常均具备的,因为这个initCause()方法是从Throwable继承的。
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");
//异常连
c.initCause(e);
throw c;
}
}
public static void main(String[] args) {
try {
g();
} catch (ExceptionC e) {
e.printStackTrace();
}
}
}
(5)finally
Try...finally结构也是保证资源正确关闭的一个手段。如果你不清楚代码执行过程中会发生什么异
常情况会导致资源不能得到清理,那么你就用try对这段"可疑"代码进行包装,然后在finally中进行
资源的清理。
public void readFile(String file) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(
new FileInputStream(file)));
// do some other work
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
(6)自定义异常类
创建Exception或者RuntimeException的子类即可得到一个自定义的异常类。例如:
public class MyException extends Exception{
public MyException(){}
public MyException(String smg){
super(smg);
}
}
使用自定义的异常
用throws声明方法可能抛出自定义的异常,并用throw语句在适当的地方抛出自定义的异常。例如:
在某种条件抛出异常
void test1() throws MyException{
if(....){
throw new MyException();
}
}
将异常转型(也叫转译),使得异常更易读易于理解
public void test2() throws MyException{
...
try{
...
}catch(SQLException e){
...
throw new MyException();
}
}
还有一个代码,很有意思:
public void test2() throws MyException{
...
try {
...
} catch (MyException e) {
throw e;
}
}
这段代码实际上捕获了异常,然后又和盘托出,没有一点意义,如果这样还有什么好处理的,不处理就行了,
直接在方法前用throws声明抛出不就得了。异常的捕获就要做一些有意义的处理。