Java基础——方法的异常说明throws关键字在类的继承中的限制规则

定义

引用《Java 编程思想》中文版第四版对throws的说明:

“异常说明属于方法声明的一部分,紧跟在形式参数列表之后,使用了附加的关键字throws,后面接一个所有潜在异常类型的列表。”


问题一

那么既然对“方法潜在异常类型”声明在了方法形式参数后,那么是否在继承中子类重写父类方法时,子类的方法声明后面也要受父类方法的约束抛出父类指定的异常?

测试

public class BaseException extends Exception {
    
}
public class SubException extends BaseException {
    
}

 以上代码自定义一个异常类,并派生出一个子类

public abstract class SuperTest {

    public abstract void testMethodA() throws BaseException;
    public abstract void testMethodB() throws BaseException;
    public abstract void testMethodC() throws BaseException;
    public abstract void testMethodD() throws BaseException;

}
public class Test extends SuperTest {
    @Override
    public void testMethodA() throws BaseException {
        //完全遵循父类方法约束,编译能通过
    }

    @Override
    public void testMethodB() {
        //并没有抛出父类方法的BaseException,编译能通过
    }

    @Override
    public void testMethodC() throws BaseException, IOException {
        //此处会报编译错误,父类方法并没有抛出IOException异常
        //'testMethodC()' in 'com.example.Test'
        // clashes with 'testMethodC()' in 'com.example.SuperTest';
        // overridden method does not throw java.io.IOException
    }

    @Override
    public void testMethodD() throws SubException {
        //子类方法抛出的是父类方法抛出的异常类的子类,编译能通过
    }
}

通过对以上四个方法的测试,可以得出部分结论:

  • 子类可以完全参照父类的异常说明与父类保持一致;
  • 子类可以不声明任何异常说明,即便它的父类声明了异常说明;
  • 子类不能声明父类没有声明的异常类型(编译器无法通过);
  • 对上一条稍做修正,子类可以声明父类声明的异常类的子类。

问题二

我们知道,在Java中构造方法也属于一类特殊的方法,那么构造方法的异常说明是否也遵循上述规则呢?

测试

public abstract class SuperTest {

    public SuperTest() throws BaseException{

    }
}

以上代码父类的构造方法声明抛出BaseException类型异常,下面做几种尝试:

public class Test extends SuperTest {

    public Test() throws BaseException {
        //通过编译
    }

}
public class Test extends SuperTest {

    public Test(){
        //编译错误:Unhandled exception: com.example.BaseException
    }

}
public class Test extends SuperTest {

    public Test() throws BaseException, IOException{
        //子类构造方法异常类型新增java.io.IOException,也能通过编译
    }

}
public class Test extends SuperTest {

    public Test() throws SubException{
        //子类构造方法异常类型是父类构造方法声明的异常类的子类时,也报编译错误:
        //Unhandled exception: com.example.BaseException
    }

}

以下方式尝试捕获异常:

public class Test extends SuperTest {

    public Test() {
        //尝试在子类构造方法中去捕获父类构造方法的异常时编译错误:Unhandled exception: com.example.BaseException
        try {
            //super()方法必须写在此构造方法的第一行,否则编译不通过
            super();//
        } catch (BaseException e) {
            e.printStackTrace();
        }
    }

}

好!根据以上几组测试,得出构造方法和普通方法对继承中的异常说明限制规则有所不同:

  • 子类的构造方法必须声明父类构造方法已经声明的异常类型;
  • 子类的构造方法可以声明任何新的异常,不受父类限制;
  • 子类的构造方法不能捕获父类构造方法抛出的异常。

问题三

还有一种情况就是,当一个子类的一个方法同时重写了父类的方法同时实现了接口方法,即父类方法和接口方法相同时,该如何限制子类方法的异常说明?

测试

定义存在相同方法名的一个接口和一个父类:

public interface ITest {

    void testMethodA();
    void testMethodB() throws BaseException;
    void testMethodC() throws BaseException;

}
public abstract class SuperTest {

    public abstract void testMethodA() throws BaseException;
    public abstract void testMethodB();
    public abstract void testMethodC() throws BaseException;

}

子类同时继承和实现:

public class Test extends SuperTest implements ITest{

    @Override
    public void testMethodA() throws BaseException{
        //接口方法没有声明BaseException类型的异常说明,但父类方法声明了
        //编译报错:
        // 'testMethodA()' in 'com.example.Test' clashes with 'testMethodA()' 
        // in 'com.example.ITest'; overridden method does not throw
        //'com.example.BaseException'
    }
    
    @Override
    public void testMethodB() throws BaseException{
        //父类方法没有声明BaseException类型的异常说明,但接口方法声明了
        //编译报错:
        //'testMethodB()' in 'com.example.Test' clashes with 'testMethodB()' 
        // in 'com.example.SuperTest'; overridden method does not throw
        //'com.example.BaseException'

    }

    @Override
    public void testMethodC() throws BaseException {
        //接口方法和父类方法都声明了BaseException类型的异常说明
        //编译通过
    }

}

以上代码可见:

  • 子类方法同时实现接口方法和重写父类方法时对于异常说明的约束同时收到接口方法的声明和父类方法的声明的限制。

总结

一个出现在父类方法的异常说明,不一定会出现在子类方法的异常说明中,这种情况与继承的规则明显不同,但普通方法的异常说明受父类方法的限制不能声明父类异常类及其子类以外的异常类型。

构造方法是个例外,必须声明父类构造方法中声明的异常说明,但可以在此基础上新增抛出的异常类型。

此外,特别注意当接口方法和父类方法相同的情况下,子类的异常说明同时受到接口方法和父类方法的制约。

你可能感兴趣的:(Java基础知识,Java异常处理,throws,异常的限制,方法继承重写,异常说明)