子类重写父类方法时,抛出的受检异常不能比父类更宽泛,只能抛出父类异常的子类或相同类型。具体做法是子类声明的异常集合必须是父类声明异常集合的子集,若需处理更广泛的异常,应在子类内部捕获并转为运行时异常或包装处理。

子类抛出比父类更宽的异常会编译报错吗
会直接导致编译错误。Java 编译器强制要求重写的受检异常范围不得超出父类定义的范围,这是为了保证多态调用的安全性,确保调用者按父类接口编程时能捕获到所有预期的异常。

public class Parent {
public void doWork() throws IOException {
throw new IOException("parent error");
}
}
public class Child extends Parent {
@Override
public void doWork() throws Exception { // Compile Error: Exception is not compatible with throws clause in Parent.doWork()
throw new Exception("child error");
}
}
上述代码中,Child 试图抛出 Exception,而父类仅声明了 IOException。由于 Exception 是 IOException 的超类,范围更广,编译器会拒绝编译。正确的做法是让子类抛出 IOException 或其子类,例如 FileNotFoundException。
立即学习“Java免费学习笔记(深入)”;
子类不抛出任何异常合法吗
完全合法。子类可以选择不抛出任何受检异常,即使父类方法声明了受检异常。这意味着子类实现认为该操作在当前上下文中不会失败,或者它会在内部处理这些异常情况而不向外传播。

public class Parent {
public void doWork() throws IOException {
throw new IOException("parent error");
}
}
public class Child extends Parent {
@Override
public void doWork() {
// No throws clause, valid
System.out.println("working without exception");
}
}
public class Test {
public static void main(String[] args) {
Parent p = new Child();
try {
p.doWork();
} catch (IOException e) {
// This block might never be reached if Child doesn't throw it
e.printStackTrace();
}
}
}
在 Test 类中,尽管通过父类引用 p 调用 doWork() 并尝试捕获 IOException,但由于实际执行的是 Child 的方法,该方法并未抛出异常,因此 catch 块不会被触发。这种写法在逻辑上是安全的,但调用者仍需按照父类契约编写异常处理代码,以防未来子类实现发生变化。
子类抛出父类异常的子类如何处理
这是最推荐的重写方式。子类可以抛出父类声明异常的任意子类,这符合 Liskov 替换原则,因为调用者捕获父类异常时也能覆盖子类异常。例如,父类抛出 Exception,子类可以抛出 RuntimeException(非受检)或 SQLException(受检且为 Exception 子类)。
import java.sql.SQLException;
public class DatabaseParent {
public void connect() throws Exception {
throw new Exception("connection failed");
}
}
public class MySQLChild extends DatabaseParent {
@Override
public void connect() throws SQLException {
throw new SQLException("mysql connection failed");
}
}
public class Client {
public static void main(String[] args) {
DatabaseParent db = new MySQLChild();
try {
db.connect();
} catch (Exception e) {
// Catches both Exception and its subclasses like SQLException
System.out.println("Caught: " + e.getClass().getName());
}
}
}
运行结果将打印 Caught: java.sql.SQLException。客户端代码只需捕获 Exception 即可处理所有情况,无需修改。如果子类抛出的是非受检异常(如 RuntimeException),则调用者不需要显式捕获,但依然可以通过 try-catch 块进行捕获,这同样符合规范。


















