在 Java 中,方法重写(Overriding)时,抛出异常的规则可以总结为:抛出的异常不能比父类方法多,只能比父类方法少。这遵循了Liskov替换原则,即子类对象应当可以替换父类对象,而不改变程序的预期行为。具体地,这个规则体现在以下几个方面:
1. 受检异常(Checked Exception)
受检异常是指那些必须在方法签名中声明的异常,如 IOException
、SQLException
等。这些异常被认为是可恢复的,需要被显式地捕获或声明。
在方法重写时,子类重写方法不能抛出比父类方法更多的受检异常。也就是说,子类方法声明的异常集合应该是父类方法异常集合的子集,即不能增加受检异常。
例如,父类方法抛出了 IOException
,子类方法可以选择不抛出异常,或者抛出更具体的异常(如 FileNotFoundException
),但不能抛出比父类更多的异常(如 SQLException
)。
// 父类
class Parent {
public void readFile() throws IOException {
// 读取文件的代码
}
}
// 子类重写
class Child extends Parent {
@Override
public void readFile() throws FileNotFoundException { // 只抛出更具体的异常(受检异常是允许的)
// 读取文件的代码
}
}
// 子类重写(错误示例)
// 如果子类抛出更多的受检异常,编译时会报错
class ChildError extends Parent {
@Override
public void readFile() throws SQLException { // 错误:不能抛出比父类更多的异常
// 读取文件的代码
}
}
2. 运行时异常(Unchecked Exception):
// 父类
class Parent {
public void doSomething() {
// 一些操作
}
}
// 子类重写
class Child extends Parent {
@Override
public void doSomething() {
// 可以抛出运行时异常,即使父类方法没有声明
throw new NullPointerException("发生了空指针异常");
}
}
3. 为什么有这些规则?
这些规则的设计是为了确保子类能够替代父类,且不会影响父类的原有行为。如果子类方法声明了更多的异常,调用者在使用子类时,可能会遇到比父类更多的异常,增加了调用者的处理复杂性。而运行时异常本身不要求强制捕获,因此可以在子类中引入新的运行时异常,不会影响到使用者的代码结构。
总结:
- 受检异常(Checked Exception):子类方法可以抛出比父类方法更少的受检异常,但不能抛出更多的受检异常。
- 运行时异常(Unchecked Exception):子类方法可以抛出新的运行时异常,不受限制。
这两个规则保证了程序的可维护性和一致性,遵循了 Liskov 替换原则:在运行时,你应该能够使用父类或子类对象,而不影响程序的正常行为。
有问题欢迎大佬来指出~