四.继承中异常
1. 关于构造函数中的异常
1.1 构造函数中的异常规则
某个derivedclass构造函数的“异常规格接口“可以比其所调用的父类的构造函数的异常规格接口宽,但决不能变窄。
1) derivedclass的构造函数必须在自己的异常规格中声明所有baseclass构造函数的异常规格中所声明的异常。
2) 在derivedclass的构造函数的异常规格中还可以声明新的异常,即声明在baseclass构造函数的异常规格中没有声明的异常。
1.2 原因
当在产生一个derivedclass的对象时,会在derivedclass的构造函数中调用baseclass的构造函数(初始化过程请见第6章),所以在derivedclass的构造函数中可能会抛出baseclass构造函数的异常规格中声明的异常,因此要在derivedclass的异常规格中声明baseclass构造函数的异常规格中声明的异常。
**:如果调用的函数的异常规格中声明了异常,那么在调用该函数的时候要捕捉它的异常规格中声明的异常。但在derivedclass构造函数中却无法捕捉其baseclass构造函数所掷出的异常。
2. 关于非构造函数的异常规则
2.1 某个函数的“异常规格接口“在继承和重载中可以变窄,但决不能变宽
要覆写baseclass的函数时,如果被覆写函数(baseclass中的函数)的异常规格中声明了异常,那么覆写函数(derivedclass中覆写了baseclass中的函数的那个函数)的异常规格中可以声明(1)与被覆写函数完全相同的异常;(2)被覆写函数异常规格中的部分异常或其子类异常;(3)不声明异常规格。
2.2 原因
这么做是为了满足“能处理被覆写函数的代码,不用做任何修改就能处理覆写函数的代码”的原则。
如果覆写函数的异常规格中声明了在被覆写函数的异常规格中不存在的异常,那么能处理被覆写函数的代码就不能处理覆写函数,因为没有捕捉覆写函数中不存在于被覆写函数中的异常声明。
import java.sql.SQLException;
class BaseClass{
public void f(){}
}
class DerivedClass1 extends BaseClass{
//public void f() throws SQLException {}(1)
public void f() {}//(2)
}
public class Test{
public static void f(BaseClass bc) { bc.f(); }
/*(3)
public static void f(BaseClass bc) {
try{
bc.f();
}
catch(SQLException ex){}
}
*/
public static void main(String[] args){
BaseClass bc = new BaseClass();
f(bc);
DerivedClass1 dc = new DerivedClass1();
f(bc);
}
}
如果允许“异常接口“变宽,我们看看上面代码会出现什么结果。首先,我们可以将代码(1)的注释去掉,并注释掉代码(2)。由于BaseClassclass中的被覆写f()函数没有声明异常规格,而代码(1)中覆写f()函数声明了,那么Testclass中的f(BaseClass bc)虽然能处理被覆写f()函数的调用,但不能处理覆写f()函数的调用,因为代码覆写f()函数声明了异常规格,而f(BaseClass bc)没有进行捕捉。那么为了处理覆写f()函数,我们还要编写代码(3)那样的处理函数。
2.3 产生对象的异常规则
在产生一个对象时,捕捉的是产生对象时所调用的构造函数中所声明的异常。
2.4 函数调用时的异常规则
1) 当把一个对象向上转型为它的baseclass时,并通过转型后的reference进行函数调用时,我们要捕捉的是其baseclass的异常声明。
2) 当用对象的原始类型来调用函数时,只需捕捉所调用的覆写函数的异常
2.5 继承中的异常规则的一个实例
import java.lang.Exception;
class BaseException extends Exception {}
class Derived1Exception extends BaseException {}
class Derived2Exception extends BaseException {}
class Derived11Exception extends Derived1Exception {}
class BaseClass{
BaseClass() throws Derived1Exception {}
BaseClass(int i) throws BaseException {}
BaseClass(int i, int j) {}
//在覆写f()时不能声明异常规格
public void f() {}
//在覆写g()时可以不声明异常或声明BaseException异常或声明
//BaseException异常的子类
public void g() throws BaseException {}
//在覆写u()时可以不声明异常
public void u() throws Derived1Exception, Derived2Exception {}
}
class DerivedClass1 extends BaseClass{
//baseclass构造函数中声明了异常的处理方法
//声明与baseclass构造函数中的异常完全相同的异常
DerivedClass1(int i) throws Derived1Exception {}
//声明baseclass构造函数中的异常的父类异常
DerivedClass1() throws BaseException {}
//声明baseclass构造函数中的异常和新的异常
DerivedClass1(String s) throws Derived1Exception, Derived2Exception{}
//声明baseclass构造函数中的异常父类异常和新的异常
DerivedClass1(String s, int i) throws BaseException, Derived2Exception{}
DerivedClass1(int i, int j) { super(i, j); }
//注意下面这两句
DerivedClass1(int i, String s) throws BaseException { super(i); }
//!DerivedClass1(int i, String s) throws Derived1Exception { super(i);}
public void f() {}
//下面覆写g()的几种方式
//不声明
//public void g() {}
//public void g() throws BaseException {} 声明完全相同
//声明子类异常
public void g() throws Derived1Exception {}
//声明子类异常
//public void g() throws Derived1Exception, Derived11Exception {}
//下面覆写u()的几种方式
//public void u() {}不声明
//public void u() throws Derived11Exception {}声明部分异常的异常
//public void u() throws Derived1Exception {}声明部分异常
//声明完全相同
//public void u() throws Derived1Exception, Derived2Exception {}
//声明子类异常
//public void u() throws Derived1Exception, Derived11Exception {}
}
public class Test{
public static void main(String[] args){
//捕捉的是相应的构造函数的异常
try{
BaseClass bc1 = new DerivedClass1();
}
catch(BaseException be) {}
try{
BaseClass bc2 = new DerivedClass1("bc2");
}
catch(Derived1Exception be1) {}
catch(Derived2Exception be2) {}
//通过向上转型来调用函数
BaseClass bc3 = new DerivedClass1(1, 1);
/*捕捉的是父类中被覆写的函数的异常,而这里捕捉的是子类中
* 被覆写的函数的异常,所以编译错误
try{
bc3.g();
}
catch(Derived1Exception be) {}
*/
//捕捉了被覆写函数的异常,是正确的
try{
bc3.g();
}
catch(BaseException be) {}
//用对象的原始类型来调用函数
DerivedClass1 dc1 = new DerivedClass1(1, 1);
//只需捕捉所调用的覆写函数的异常
try{
dc1.g();
}
catch(Derived1Exception be) {}
}
}