异常的定义:在《java编程思想》中这样定义异常:阻止当前方法或作用域继续执行的问题。
异常类的来源:一是java语言定义的一些基本异常;二是用户自定义的一些异常,这些异常继承Exception类或其子类。
异常对象的来源:一是java运行时环境抛出的异常;二是程序员自己手动抛出的异常,这种异常可以使程序员自己定义的,也可以是java语言定义的,通过throw关键字来抛出这种指定的异常,这种异常用来向调用者汇报异常的一些信息。
Java处理异常的5个关键字:try catch finally throw throws。在try语句中包含可能抛出异常的代码,在catch中进行匹配并捕获相应的异常对象,finally表示这段代码必须执行,一般用于释放一些物理资源,throw表示明确的抛出一个异常,在方法签名处通过throws来表明可能抛出的各种异常。
在java程序执行try块 catch块时遇到了return或throw语句,这两条并不是立即执行,首先看看是否含有finally部分,如果有,先执行finally部分,再跳回到try或catch块里的return或throw语句,如果没有finally部分,程序立即执行try或catch块里的return或throw语句。若finally里面也有return或throw语句,系统将不会再跳到try或catch块里面。
一般结构:
try{
程序代码
}catch(异常类型1 异常的变量名1){
程序代码
}catch(异常类型2 异常的变量名2){
程序代码
}finally{
程序代码
}
catch可以有多个,按异常类从小到大进行排列,try语句中抛出的异常对象只要在catch中有一个匹配成功,就不再进行往下的匹配。Catch括号中的异常类可以是java语言定义的类也可以是自己定义的类,异常变量名表示抛出异常类对象的引用,可以在catch代码块中直接使用这个变量名,通过这个引用(变量名)的方法来获得异常对象的详细信息。
常见异常继承关系:
Exception表示程序本身可以处理的异常
Error一般是指虚拟机相关的问题,如系统失败,动态链接失败等,这种错误无法恢复或捕获,程序将中断。所以不应该捕获这种error错误。
RunttimeExceptionJava 虚拟机正常运行期间抛出的异常的超类。Java编译器不去检查它,也就是说,当程序中可能出现这类异常时,即使没有用try...catch语句捕获它,也没有用throws字句声明抛出它,还是会编译通过,这种异常可以通过改进代码实现来避免。
处理异常一般用两种方式:
第一:在catch语句中处理
第二:对于处理不了的异常用throw抛出指定异常,并在方法签名处通过throws抛出,让上一级的调用者来处理,如果main方法也不知道如何处理,也使用throws方法抛出异常,该异常将交给虚拟机来处理。Java虚拟机会从出现异常的方法代码 块中往上找,直到找到处理该异常的代码块为止。然后将异常交给相应的catch语句处理。如果Java虚拟机追溯到方法调用栈底部main()方法时, 如果仍然没有找到处理异常的代码块,将按照下面的步骤处理:
(1) 调用异常的对象的printStackTrace()方法,打印方法调用栈的异常信息。
(2) 如果出现异常的线程为主线程,则整个程序运行终止;如果非主线程,则终止该线程,其他线程继续运行。
通过分析思考可以看出,越早处理异常消耗的资源和时间越小,产生影响的范围也越小。因此,不要把自己能处理的异常也抛给调用者。
并且注意try语句不能单独存在,可以和catch、finally组成 try...catch...finally、try...catch、try...finally三种结构,catch语句可以有一个或多 个,finally语句多一个,try、catch、finally这三个关键字均不能单独使用。throw语句后不允许有紧跟其他语句,因为这些没有机会执行。
一个方法可能抛出异常的标志是:
1 在方法签名处有throws语句
2 在方法中含有throw语句
自定义异常类
创建Exception或者RuntimeException的子类即可得到一个自定义的异常类。例如:
public class MyException extends Exception{
public MyException(){}
public MyException(String smg){
super(smg);
}
}
Exception类可以分为两种:运行时异常和受检查异常。
1、运行时异常
RuntimeException 类及其子类都被称为运行时异常,这种异常的特点是Java编译器不去检查它,也就是说,当程序中可能出现这类异常时,即使没有用try...catch语 句捕获它,也没有用throws字句声明抛出它,还是会编译通过。例如,当除数为零时,就会抛出 java.lang.ArithmeticException异常。
2、受检查异常
意味着程序出现了bug,如数组越界,0被除等。除了RuntimeException类及其子类外,其他的Exception类及其子类都属于受检查异常,这种异常的特点是要么用try...catch捕获处理,要么用throws语句声明抛出,否则编译不会通过。
3、两者的区别
运行时异常表示无法让程序恢复运行的异常,导致这种异常的原因通常是由于执行了错误的操作。一旦出现错误,建议让程序终止。
受检查异常表示程序可以处理的异常。如果抛出异常的方法本身不处理或者不能处理它,那么方法的调用者就必须去处理该异常,否则调用会出错,连编译也无法通过。当然,这两种异常都是可以通过程序来捕获并处理的。
4、运行时错误
一般很少见,也很难通过程序解决。它可能源于程序的bug,但一般更可能源于环境问题,如内存耗尽。错误在程序中无须处理,而有运行环境处理。Error类及其子类表示运行时错误,通常是由Java虚拟机抛出的。程序本身无法修复这些错误.一般不去扩展Error类来创建用户自定义的错误类。而RuntimeException类表示程序代码中的错误,是可扩展的,用户可以创建特定运行时异常类。
Error(运行时错误)和运行时异常的相同之处是:Java编译器都不去检查它们,当程序运行时出现它们,都会终止运行。
5、佳解决方案
对于运行时异常,我们不要用try...catch来捕获处理,而是在程序开发调试阶段,尽量去避免这种异常,一旦发现该异常,正确的做法就会改进程序设计 的代码和实现方式,修改程序中的错误,从而避免这种异常。捕获并处理运行时异常是好的解决办法,因为可以通过改进代码实现来避免该种异常的发生。
对于受检查异常,没说的,老老实实去按照异常处理的方法去处理,要么用try...catch捕获并解决,要么用throws抛出!对于Error(运行时错误),不需要在程序中做任何处理,出现问题后,应该在程序在外的地方找问题,然后解决。
程序发生异常情况时,首先,同其它java对象的创建一样,在堆中new出一个异常对象。然后,当前的执行路径被终止,如果出现异常的线程为主线程,则整个程序运行终止;如果非主线程,则终止该线程,其他线程继续运行。同时从当前环境中抛出该异常对象的引用。此时,异常处理机制接管程序,并寻找一个合适的地方继续执行程序,即异常处理程序,它的任务是将程序从错误中恢复。
在new出异常对象的时候,随之进行的是存储空间的分配和构造器的调用。所有的标准异常类都有两个构造器:一个是默认的构造器;另一个是接受字符串作为参数,以便将相关信息放入异常对象的构造器。因为异常对象是在堆中new出的,所有垃圾回收器会自动将它们清理到。
对于错误的信息可以保存在异常对象的内部或用异常对象的类名来进行暗示。
有时可能用不到标示符,因为异常的类型已经足够暗示异常的信息,但标示符不能省略。
我们平时所关心的异常的基类通常是Exception异常。
抛出异常时,异常处理系统会按顺序查找“近”的catch,一旦找到匹配的处理catch,就不再继续匹配了。对于类型的匹配,派生类的对象也可以匹配其基类的处理程序。
异常处理的优点之一就是是得我们可以在某处集中精力处理要解决的问题,而在另一处处理编写的这段代码中产生的错误。