假设:
static class A {
void process() throws Exception { throw new Exception(); }
}
static class B extends A {
void process() { System.out.println("B "); }
}
public static void main(String[] args) {
A a = new B();
a.process();
}
在这个问题中,当我调用a.process()
时,它会给我一个编译时错误,说“必须处理未处理的异常”。但是,如果父方法抛出任何已检查的异常,如果我们覆盖父级的实现,则不必在子级中处理该异常。
为什么仍然检查异常?
答案 0 :(得分:4)
重点是编译器知道你正在调用一个不会抛出任何已检查异常的重写方法。当它看到:
a.process();
它“不知道”a
的值实际上是对B
实例的引用。它可以是对A
(或另一个子类的实例)的实例的引用,将抛出异常。
如果要使用特定于子类的签名,则需要:
B b = new B();
代替。然后当编译器看到
时b.process();
它将查看如何在B
中声明方法,而不是如何在A
中声明方法。
答案 1 :(得分:1)
如果父方法抛出异常,则抛出Child:
在您的情况下,您声明了一个可以引用
的A类型的对象因此,当您调用a.process();
时,编译器会检查在A中声明的方法定义,并且由于A中的方法进程声明它会抛出异常,因此编译器会抱怨未处理的异常
答案 2 :(得分:0)
嗯, Jon Skeet (一如既往:P)给出了一个非常好的答案。在这里加两分钱。
A a = new B();
a.process();
在上面的行中,编译器只查找类A
(引用)并检查方法process()
是否存在。它还检查签名并验证在调用方法时抛出的异常是否正确处理。
这里的编译器只看到A类的process()
。它甚至不检查class B
是否定义/声明process()
。调用哪个process()
由JVM决定。因此,编译器会看到它看到的内容,类A的进程抛出异常,所以你必须处理它。
答案 3 :(得分:0)
你得到了编译错误,因为你已经声明你的对象是A类型.A级的.process()方法的声明说它可能引发异常,所以编译器说你应该处理它。编译器无法知道(*)在调用发生时,您的对象a实际上是A的子类型。
与你所说的相反,重写方法完全有可能不抛出在超类型级别声明可能的任何异常(或者只是其中一些但不是全部,或者声明可能的那些异常的某些正确子类型)超类型。)
(*)代码可以比这些简单的两个语句复杂得多。