1.Java异常处理
Java是面向对象的语言,所以在Java语言中万物皆对象、处处皆对象。在异常处理中,所谓的异常在Java程序中就是一个异常对象。而该对象可以是系统定义好的类对象,也可以是程序员自己定义的异常类对象,总之这些异常都是对象
在发生异常时,JVM会引发一系列行为。首先正如产生普通对象一样在堆栈上创建一个异常对象,而该对象就是某个异常类的实例,该类是Java类库或程序员已经定义好的,每一种异常类对应一种情况的异常类型,类中可以包含该异常错误的相关信息和处理异常的方法等内容,所以对于程序抛出的异常对象总有一个异常类与之对应。一旦异常抛出,程序停止当前的代码执行,接着抛出那个异常对象的引用,异常处理代码会接收该异常对象,如果找到处理该异常类型的代码,则处理异常,否则程序将继续把该异常抛向更外层的环境去处理,如果不能处理则最终交给操作系统,从而终止该程序的运行。
(1)try区块
java的异常机制把工作代码和异常处理代码分隔开,使程序结构清晰。工作代码集中于用户需要解决的问题,而异常处理代码则集中处理发生的异常事件。try区块就是放置可能产生异常的工作代码的区域。该区域也称为“警戒区”,意思是这里面的程序代码可能发生问题,一旦发生问题则必须有相应的处理措施。使用try关键字设置代码警戒区很简洁,就是将工作代码放在try关键字后一个花括号“{}”内,如下所示:
try{
//可能产生异常的代码1;
//可能产生异常的代码2;
}
try区块一般放在函数的内部。可以考虑这样一个问题。某个函数可能发生异常,但是又不希望异常发生时函数退出执行,所以就需要用try区块把函数内可能发生异常的代码包裹起来,这样一旦异常发生,函数内try区块后面的程序代码依旧可以继续执行。
(2)catch区块
catch区块处理发生的异常。一旦程序发生异常则抛出异常对象,该对象在异常处理函数处得到处理,这个异常处理函数就是紧跟在try区块后的catch区块。其语法格式如下所示:
try{
Thread thread=new MyThread();
......
}catch(异常类型1 对象1){
//处理异常类型1的代码
}catch(异常类型2 对象2){
//处理异常类型2的代码
}catch(异常类型3 对象3){
//处理异常类型3的代码
}
......
//程序中其它代码
......
catch子句紧跟在try区块后,一旦有异常抛出,则首先创建该异常类的实例对象,系统把该对象引用引导到异常处理代码,搜索第一个参数对象类型和该异常类型相符合的处理程序,引入该catch自己的处理代码。异常处理完成后,程序跳出try区块,直接运行紧跟其后的catch区块后的代码。
注意:
catch区块有参数类型,该参数是异常对象引用类型,指明该catch子句可以处理的异常类型,只有相符合的异常类型才得到处理,否则继续寻找符合条件的处理程序。如果最后没有找到相应的处理程序,则程序会把异常抛向更大的运行环境,知道操作系统终止该程序执行为止。
另外,在程序遇到异常发生时,会调转去执行catch子句,异常处理完毕后不会返回原先的异常发生点,所以异常发生语句后的代码得不到执行,异常处理中通常需要考虑做一些和系统有关的处理工作。
(3)Java异常规范
如果你设计的方法是给他人使用的,并且该方法可能会抛出几种异常,此时就必须理解Java的异常规范。方法的设计者必须让使用者知道你所设计的方法可能抛出怎样的异常类型。这样调用者就知道如何捕捉这些可能的异常。在Java中提供了规范的语法让方法的设计者告知用户可能抛出的异常,这就是所谓的异常规范,它属于方法声明部分,在函数的参数之后,通过关键字throws即可能引发异常类型实现。这样设计的方法如下:
private void foo() throws Exception1,Exception2,Exception3{
//函数主体
}
其中Exception1,Exception2,Exception3可以是系统提供的运行期异常,也可以是用户定义的异常。
一旦这样设计的函数引发了异常,该异常必须被处理,否则编译器会检测出来,并强迫你必须处理异常,或者明确指出该异常的异常规格,即Java允许在方法名后面直接声明要抛出的异常,一旦方法中发生异常则抛出该类型的异常,并有默认的方法处理该异常或打印相应的消息。
2.Throwable类及其子类
正如Java的所有对象都继承自Object一样,Java的所有的异常类都继承自类Throwable,该类定义了一些方法可以打印异常的描述信息。
(1)Throwable类的定义和方法
类Throwable继承自Object,Java所有的异常类都继承自Throwable。
类的定义:
public class Throwable extends Object implements Serializable.
该类是Object的子类,实现了Serializable接口,类Throwable是所有错误类和异常类的父类,该类的实例(异常对象)只能由JVM抛出或通过throw声明抛出。
Throwable类的构造函数:
Throwable() 这是默认的无参数构造函数,但使用该构造函数创建的对象调用getMassage()方法时,不返回任何值。
Throwable(String message):带String类型参数的构造函数,使用该构造函数创建的对象将保存参数信息,当对象调用getMassage()方法时,返回具体描述信息的参数值message.
Throwable类的方法:
getMassage():该方法返回Throwable的详细消息字符串,该返回值允许是null(无参数构造函数创建的对象就会出现这种情况)
getLocalizedMessage():该方法创建Throwable的本地描述,即子类可以覆盖该方法以描述自己的异常消息,如果子类不覆盖该方法,则调用getMessage()方法时和父类返回的消息字符串一样。
printStackTrace():该方法在标准输出设备上打印堆栈的轨迹,直观的解释是一旦异常在某个函数内被触发,则堆栈轨迹就是该函数被层次调用的过程显示
toString():该方法返回一个描述异常的简短字符串。如果该异常对象创建时,以非空消息字符串作为参数,则结果是3个字符串的链接,如下所示:
Java.io.FileNotFoundException:ExceptionTest.java(系统找不到指定的文件)
即由异常类名、符号“:”和异常对象调用的getMessage()方法返回值3部分组成
getStackTrace():该方法返回一个堆栈轨迹元素数组,栈顶是该异常触发的第一个方法,栈底是该异常触发的最后一个方法,一般情况下,该方法返回的数组包含
printStackTrace()打印出的所有堆栈元素
fillInStackTrace():该方法返回一个特殊的异常对象,该对象代替被重新抛出的异常,其堆栈轨迹信息将不包含重新抛出的异常信息,即在异常重新抛出时调用该方法,在
printStackTrace()方法打印的层层函数调用是从异常抛出点的函数开始的。
调用Throwable类方法示例:
package LianXi;
//该方法抛出异常,并捕获异常,然后调用Throwable类的方法
public class ThrowableMethodsTest {
private void getThrowable(){
try{
throw new Throwable("this is throwable");
}catch(Throwable ex){
System.out.println("caught Throwable!");
System.out.println("ex.getMessage():"+ex.getMessage());
System.out.println("ex.toString():"+ex.toString());
System.out.println("ex.printStackTrace():");
ex.printStackTrace();
StackTraceElement[] stackElement =ex.getStackTrace();
System.out.println("stackElement[]:");
for(int i=0;i<stackElement.length;i++){
System.out.println("stackElement["+i+"]:"+stackElement[i].toString());
}
}
}
public static void main(String[]args){
//创建类ThrowableMethodsTest()对象,并调用getThrowable()方法,测试异常捕获信息
new ThrowableMethodsTest().getThrowable();
}
}
运行效果:
caught Throwable!
ex.getMessage():this is throwable
ex.toString():java.lang.Throwable: this is throwable
ex.printStackTrace():
java.lang.Throwable: this is throwable
stackElement[]: at LianXi.ThrowableMethodsTest.getThrowable(ThrowableMethodsTest.java:6)
at LianXi.ThrowableMethodsTest.main(ThrowableMethodsTest.java:22)
stackElement[0]:LianXi.ThrowableMethodsTest.getThrowable(ThrowableMethodsTest.java:6)
stackElement[1]:LianXi.ThrowableMethodsTest.main(ThrowableMethodsTest.java:22)
代码说明:
分析一下堆栈轨迹元素,该轨迹就是发生异常的函数层层调用的关系,堆栈顶是发生该异常的函数getThrowable(),因为堆栈只有两个元素,所以第二个也就是堆栈底,是调用函数getThrowable()的main()方法。显然,printStackTrace()方法打印的消息就是堆栈中的堆栈元素。
(2)异常类的继承关系
Java所有的异常类都继承自Throwable,该类有两个子类:一个是Error,另一个是Exception。Error类表示系统错误或编译期错误,如语法错误、函数书写错误等,一般不用。
我们经常遇到且可以操作的异常类是Exception类,这类异常是Java标准函数库中抛出的基本异常,或者是用户自定义的异常类,也可以是运行期发生的异常事件,如对象引用为null等
Java定义了众多的Exception,以其子类的形式出现。这些异常对应某一种数据操作类型错误,如IOException就是输入输出相对应的异常,ClassNotFoundException和类转载时的异常对应等。Exception的子类在不同的JDK版本中数量不同,但是功能模式是一样的,不同的异常类名称不同,所捕获的异常也有差异。每一个异常子类又有自己的子类,以处理更详细的错误。
可以捕获所有异常的catch子句:
try{
//工作代码
}catch(Exception ex){
System.out.println(ex.getMessage());
}
(3)异常重抛的例子
由于某种要求或程序环境的制约,使异常需要重新抛出才可以被捕获。如Exception异常可以捕获所有异常类型,一旦发生异常可以把该异常的引用重新抛出,使调用发生异常函数的外层环境(调用它的函数)重新捕获该异常(该异常可能是Exception的子类),如下代码所示:
catch(Exception ex){
ex.printStackTrace();
throw ex;
}
如果重新抛出,则异常会在上层环境中得以捕获
重新抛出异常示例:
public class RethrowingTest {
//定义方法One(),调用该方法则抛出Exception异常,该异常对象由带参数的构造函数创建
public static void One() throws Exception{
System.out.println("exception in one()");
throw new Exception("thrown fom One()");
}
//定义方法Two(),该方法抛出Throwable异常,方法体调用了One()方法,并捕获Exception异常,打印异常的堆栈轨迹信息,并重新抛出异常
public static void Two() throws Throwable{
try{
One();
}catch(Exception ex){
System.out.println("Inside Two(),ex.printStackTrace()");
ex.printStackTrace();
throw ex;
//重新抛出异常时调用异常的fillInStackTrace()方法,会抛出一个新的对象,该对象代替重新抛出的对象ex,该新对象只含有重新抛出
//异常点的信息,从而覆盖了对象ex的原有信息
//throw ex.fillInStackTrace();
}
}
//在main()方法中调用方法Two(),捕获方法Two()抛出的异常,并打印异常对象的堆栈信息
public static void main(String[] args) throws Throwable{
try{
Two();
}catch(Exception ex){
System.out.println("Caugth in main,e.printStackTrace()");
ex.printStackTrace();
}
}
}
运行结果:
exception in one()
Inside Two(),ex.printStackTrace()
java.lang.Exception: thrown fom One()
at LianXi.RethrowingTest.One(RethrowingTest.java:7)
at LianXi.RethrowingTest.Two(RethrowingTest.java:12)
at LianXi.RethrowingTest.main(RethrowingTest.java:25)
Caugth in main,e.printStackTrace()
java.lang.Exception: thrown fom One()
at LianXi.RethrowingTest.One(RethrowingTest.java:7)
at LianXi.RethrowingTest.Two(RethrowingTest.java:12)
at LianXi.RethrowingTest.main(RethrowingTest.java:25)
代码说明:
该类在执行过程中首先调用静态方法Two(),执行该方法体中的try/catch区块。首先调用One()函数,所以打印一条消息“exception in One()”,接着在One()方法throw关键字作用下,抛出Exception异常对象(参数为“thrown from One()”),该异常被Two()方法中的catch子句捕获,执行catch子句打印一条消息“Inside Two(),ex.printStackTrace()”,而后调用Exception异常对象的printStackTrace()方法,打印堆栈信息。
接着又抛出了异常对象,此时main()方法中的catch区块捕获该异常对象,先是输出一条消息“Caught in main,e.printStackTrace()”,随后依旧打印异常的堆栈信息。
比较发现两次输出的异常对象的堆栈信息相同,说明重新抛出的对象就是原来的对象,该对象知道异常发生的根源(发生异常的函数)。如果改变代码调用关系,一个函数调用另一个函数,异常将重新抛出。原代码改为:
public class RethrowingTest {
//定义方法One(),调用该方法则抛出Exception异常,该异常对象由带参数的构造函数创建
public static void One() throws Exception{
System.out.println("exception in one()");
throw new Exception("thrown fom One()");
}
//定义方法Two(),该方法抛出Throwable异常,方法体调用了One()方法,并捕获Exception异常,打印异常的堆栈轨迹信息,并重新抛出异常
public static void Two() throws Throwable{
try{
One();
}catch(Exception ex){
System.out.println("Inside Two(),ex.printStackTrace()");
ex.printStackTrace();
//throw ex;
//重新抛出异常时调用异常的fillInStackTrace()方法,会抛出一个新的对象,该对象代替重新抛出的对象ex,该新对象只含有重新抛出
//异常点的信息,从而覆盖了对象ex的原有信息
throw ex.fillInStackTrace();
}
}
//在main()方法中调用方法Two(),捕获方法Two()抛出的异常,并打印异常对象的堆栈信息
public static void main(String[] args) throws Throwable{
try{
Two();
}catch(Exception ex){
System.out.println("Caugth in main,e.printStackTrace()");
ex.printStackTrace();
}
}
}
代码执行结果:
exception in one()
Inside Two(),ex.printStackTrace()
java.lang.Exception: thrown fom One()
at LianXi.RethrowingTest.One(RethrowingTest.java:7)
at LianXi.RethrowingTest.Two(RethrowingTest.java:12)
at LianXi.RethrowingTest.main(RethrowingTest.java:25)
Caugth in main,e.printStackTrace()
java.lang.Exception: thrown fom One()
at LianXi.RethrowingTest.Two(RethrowingTest.java:19)
at LianXi.RethrowingTest.main(RethrowingTest.java:25)
从结果来看,当再次抛出异常时,打印该新抛出的对象堆栈信息有了变化,该对象堆栈只记住了当前环境的堆栈元素,认为异常的抛出点就是重新抛出异常点,一旦调用了fillInStackTrace()方法,该方法就返回一个新的对象,该对象只记住当前的环境信息。
重新抛出的异常对象也可以是其他类型的异常。给出一个例子程序,该程序首先创建一个异常类SimpleException,该类通过带参数的构造函数创建。函数主类中有两个方法One()和Two(),函数Two()调用函数One(),方法One()会抛出异常,该异常被方法Two()的catch子句捕获,在处理后,又重新抛出一个自定义的SimpleException类型的异常对象 ,该异常在方法main()的catch区块被捕获处理,此时该对象相对于方法One()中抛出的对象是一个全新的异常对象。
重抛不同异常类型的示例程序:
//创建一个异常类,该类实例通过带参数的构造函数创建
public class SimpleException extends Exception {
public SimpleException(String s){
super(s);
}
}
//创建主类
public class RethrowingTest {
//创建方法One(),该方法会抛出一个异常
public static void One() throws Exception{
System.out.println("exception in One()");
//抛出Exception类型的对象实例
throw new Exception("here is Exception");
}
public static void Two() throws Throwable{
try{
One();
}
//此时捕获的Exception类型的异常,它是SimpleException异常的父类,所以可以接受SimpleException的异常对象的引用
catch(Exception ex){
System.out.println("Inside Two(),ex.printStackTrace()");
//在方法Two()中打印异常对象的堆栈轨迹
ex.printStackTrace();
//重新抛出一个新的异常对象,该对象和方法One()中抛出的异常类型无关
throw new SimpleException("here is SimpleException");
}
}
public static void main(String[] args) throws Throwable{
try{
Two();
}
//在主函数中捕获Exception类型的异常,该异常是从方法Two()中重新抛出的一个自定义的新的异常对象,该异常类是Exception
//的子类,所以catch子类可以捕获
catch(Exception e){
System.out.println("Caught in main,e.printStackTrace()");
e.printStackTrace();
}
}
}
运行结果:
exception in One()
Inside Two(),ex.printStackTrace()
java.lang.Exception: here is Exception
at LianXi.RethrowingTest.One(RethrowingTest.java:8)
at LianXi.RethrowingTest.Two(RethrowingTest.java:12)
at LianXi.RethrowingTest.main(RethrowingTest.java:26)
Caught in main,e.printStackTrace()
LianXi.SimpleException: here is SimpleException
at LianXi.RethrowingTest.Two(RethrowingTest.java:20)
at LianXi.RethrowingTest.main(RethrowingTest.java:26)
代码说明:
由运行结果可见,最后抛出的是异常类SimpleException对象,它知道自己从方法Two()抛出,而和方法One()无关。本程序在执行时创建了两个异常对象,一个是Exception异常对象,另一个是自定义的SimpleException异常对象,这些对象都是通过new关键字创建的(该行为因throw关键字而自动执行),对于在堆栈中创建的异常对象而言,一旦系统不再需要都被垃圾回收器处理。
(4)运行期异常
运行期异常是不需要用户关心的异常,此类异常Java会自动执行异常检查工作,出现执行期异常则由系统自动抛出,记住编写Java程序时RuntimeException是唯一可以省略的异常。所有运行期异常都继承自RuntimeException异常类,在编写程序时,不必考虑此类异常,所有函数都默认自己可能抛出RuntimeException,系统会自动探测、捕获并处理运行期异常。
运行期异常示例程序:
//定义静态方法OneMethod(),该方法抛出RuntimeException
public class RuntimeExceptionTest {
static void OneMethod(){
throw new RuntimeException("from OneMethod()");
}
//定义静态方法TwoMethod(),该方法调用函数OneMethod()
static void TwoMethod(){
OneMethod();
}
//定义静态方法ThreeMethod(),该方法调用函数TwoMethod()
static void ThreeMethod(){
TwoMethod();
}
public static void main(String[]args){
//在主函数中调用ThreeMethod()
ThreeMethod();
}
}
运行效果:
Exception in thread "main" java.lang.RuntimeException: from OneMethod()
at LianXi.RuntimeExceptionTest.OneMethod(RuntimeExceptionTest.java:5)
at LianXi.RuntimeExceptionTest.TwoMethod(RuntimeExceptionTest.java:9)
at LianXi.RuntimeExceptionTest.ThreeMethod(RuntimeExceptionTest.java:13)
at LianXi.RuntimeExceptionTest.main(RuntimeExceptionTest.java:17)
代码分析:
该程序设计了3个方法,方法ThreeMethod()调用方法TwoMethod(),方法TwoMethod()调用方法OneMethod()。而方法OneMethod()会抛出运行期异常。
说明:
虽然没有明确定义如何处理该异常,但是系统还是探测到该异常并打印了错误消息,而且该错误消息就是异常堆栈轨迹信息,即发生异常的函数被层层调用的函数关系。事实上,如果某个RuntimeException穿过层层函数传递到main()函数而没有被捕获,则系统将终止该程序并调用该异常的printStackTrace()方法。
3.自定义异常
如果用户有这样的需求,即自己的程序可能产生某个特定类型的错误,而该错误是Java类库所没有提供的,那就需要自定义实现
自定义简单异常示例程序
import java.io.IOException;
public class SimpleException extends IOException {
}
public class SimpleExceptionTest {
public void foo() throws SimpleException{
System.out.println("在方法foo()内抛出SimpleException");
throw new SimpleException();
}
public static void main(String [] args){
try{
new SimpleExceptionTest().foo();
}catch(SimpleException ex){
System.err.println("捕获到SimpleException异常");
}
}
}
代码运行结果:
在方法foo()内抛出SimpleException
捕获到SimpleException异常
代码说明:
输出的第1行语句“在方法foo()内抛出SimpleException”说明函数foo()被调用。第二行输出语句“捕获到SimpleException异常”说明函数foo()确实抛出了异常且该异常被捕获,即该异常得到适当的处理
自定义代码示例程序2
//自定义MyException,该类有两种构造函数,一种带参数,另一种不带参数
public class MyException extends Exception {
public MyException(){}
public MyException(String message){
super(message);
}
}
//定义主类以测试带参数构造函数的自定义异常
public class ParaExceptionTest {
//定义方法first(),该方法抛出自定义的不带参数的异常对象
public static void first() throws MyException{
System.out.println("throw MyException from first()");
throw new MyException();
}
//定义方法second(),该方法抛出自定义的带参数的异常对象
public static void second() throws MyException{
System.out.println("throw MyException from first()");
throw new MyException("MyException in second()");
}
public static void main(String [] args){
//在方法main中调用静态方法first(),并捕获自定义不带参数异常,并把异常信息打印到标准错误输出装置
try{
first();
}catch(MyException myex){
myex.printStackTrace(System.err);
}
//在方法main中调用静态方法second(),并捕获自定义带参数异常,并把异常信息打印到标准错误输出装置
try{
second();
}catch(MyException myex){
myex.printStackTrace(System.err);
}
}
}
代码执行结果:
throw MyException from first()
LianXi.MyException
throw MyException from first()
at LianXi.ParaExceptionTest.first(ParaExceptionTest.java:7)
at LianXi.ParaExceptionTest.main(ParaExceptionTest.java:17)
LianXi.MyException: MyException in second()
at LianXi.ParaExceptionTest.second(ParaExceptionTest.java:12)
at LianXi.ParaExceptionTest.main(ParaExceptionTest.java:23)
代码说明:
该段程序首先创建了一个自定义异常类,其构造方法有两个:一个是带参数类,另一个是不带参数类。在抛出该异常类的实例时可以选择使用。在类ParaExceptionTest中定义了两个方法first()和second()方法,其中first()方法抛出自定义异常,该异常对象通过不带参数的构造方法创建。second()方法也抛出了自定义异常,该异常对象通过带参数的构造函数创建,一旦异常发生,堆栈轨迹信息被输出到标准错误流System.err,这使System.out被重定向
实际上,用户在编写自定义异常时可以有更大的自由度,不但可以添加构造函数,也可以添加成员函数。可以定义这样一个类,如下代码所示:
public class ExtendMyException extends Exception {
private int i;
public ExtendMyException(String message){
super(message);
}
public ExtendMyException(String message,int x){
super(message);
i=x;
}
public int getVal(){return i;}
}
该异常类的内容很丰富,其中定义了一个int型变量i和一个函数getVal()获得变量i的值。添加了该类的构造函数,该构造函数可以接受一个字符串和一个int型数据,该int型参数为类的变量i赋值。
下面依次测试用不同的构造函数创建的异常被抛出时异常对象堆栈轨道信息,异常对象内参变量的变化和函数调用情况。该程序首先创建一个自定义异常类,其构造函数有3个,分别是带一个参数的构造函数、带两个参数的构造函数和不带参数的构造函数。然后在主类中定义3个方法,分别抛出由上述3个构造函数创建的异常对象,并捕获这些异常,打印异常对象的堆栈轨迹信息,调用异常对象的各种成员。
扩展自定义异常示例程序:
//自定义扩展的异常类,添加了成员函数,静态属性,并添加了带两个参数的构造函数
public class ExtendMyException extends Exception {
private int i;
public ExtendMyException(){};
public ExtendMyException(String message){
super(message);
}
public ExtendMyException(String message,int x){
super(message);
i=x;
}
public int getVal(){return i;}
}
//定义执行测试程序的主类
public class ExtraSefException {
//定义方法first(),该方法抛出不带参数的构造函数创建的异常
public static void first() throws ExtendMyException{
System.out.println("throw ExtendedMyException from first()");
throw new ExtendMyException();
}
//定义方法second(),该方法抛出带字符串参数的构造函数创建的异常
public static void second() throws ExtendMyException{
System.out.println("throw ExtendedMyException from second()");
throw new ExtendMyException("from second()");
}
//定义方法third(),该方法抛出带两个参数的构造函数创建的异常,其中int型参数为对象的数据成员i赋值
public static void third() throws ExtendMyException{
System.out.println("throw ExtendedMyException from third()");
throw new ExtendMyException("from third()",108);
}
public static void main(String[] args){
//调用静态方法first(),捕获该方法抛出的异常,并打印异常对象堆栈轨迹信息
try{
first();
}catch(ExtendMyException ex){
ex.printStackTrace(System.err);
}
//调用静态方法second(),捕获该方法抛出的异常,并打印异常对象堆栈轨迹信息
try{
second();
}catch(ExtendMyException ex){
ex.printStackTrace(System.err);
}
//调用静态方法third(),捕获该方法抛出的异常,打印异常对象堆栈轨迹信息并调用异常对象的方法getVal(),打印获得数据属性i的值
try{
third();
}catch(ExtendMyException ex){
ex.printStackTrace(System.err);
System.err.println("ex.getVal()="+ex.getVal());
}
}
}
运行结果:
throw ExtendedMyException from first()
LianXi.ExtendMyException
throw ExtendedMyException from second()
at LianXi.ExtraSefException.first(ExtraSefException.java:7)
at LianXi.ExtraSefException.main(ExtraSefException.java:23)
LianXi.ExtendMyException: from second()
at LianXi.ExtraSefException.second(ExtraSefException.java:12)
at LianXi.ExtraSefException.main(ExtraSefException.java:29)
throw ExtendedMyException from third()
LianXi.ExtendMyException: from third()
at LianXi.ExtraSefException.third(ExtraSefException.java:17)
at LianXi.ExtraSefException.main(ExtraSefException.java:35)
ex.getVal()=108
代码说明:
在main()方法中调用third()方法时,该方法会抛出一个由带字符串参数和整型参数的构造函数创建的异常对象,通过该对象可以调用其getVal()函数,从而获得对象的数据属性信息。
4.finally子句
当程序发生异常时,在try区块内发生异常的代码之后的程序段不能继续执行,而是跳转到catch子句执行异常处理,但假如此时try区块内已经执行的代码建立了网络连接,而catch子句又没有有效关闭该链接,显然将浪费系统的资源,如缓存、端口号等。所以无论try区块是否抛出异常,都希望执行关闭数据库链接的操作,此时可以使用finally子句执行所有异常处理函数之后的动作
(1)执行finally子句
使用finally子句的异常处理区段的语法如下:
try{//可能发生异常的代码,如建立网络链接,读数据库数据,打开文件等
//可能抛出异常类型
}catch(Type1 ex){
//处理异常类型Type1
}catch(Type2 ex){
//处理异常类型type2
}catch(Type3 ex){
//处理异常类型type3
}finally{
//执行关键操作,考虑无论有无异常都要执行的动作,如关闭网络连接、关闭打开的文件等
}