Java 覆盖情况下的异常

Java 覆盖情况下的异常,java,Java,我们知道,在方法重写的情况下,如果子类方法抛出一些检查异常,那么强制父类方法应该抛出相同的检查异常或其父类异常,否则我们将得到编译错误。但没有关于未检查异常的规则 但如果假设Java允许父类方法检查异常,则该异常是子类方法检查异常的子类 有人能解释一下为什么Java中不允许这样做吗 让我们换一种方式来回答这个问题: 你有甲级- class A { public void doStuff() throws SQLException { } } B类扩展了A类- class

我们知道,在方法重写的情况下,如果子类方法抛出一些检查异常,那么强制父类方法应该抛出相同的检查异常或其父类异常,否则我们将得到编译错误。但没有关于未检查异常的规则

但如果假设Java允许父类方法检查异常,则该异常是子类方法检查异常的子类

有人能解释一下为什么Java中不允许这样做吗


让我们换一种方式来回答这个问题:

你有甲级-

class A {

    public void doStuff() throws SQLException {

    }
}
B类扩展了A类-

class B extends A {

    public void doStuff() throws Exception {

    }
}
由于违反了方法的约定,它将在编译期间引发异常


假设Java允许这样做,那么会有什么后果呢?

如果覆盖没有抛出声明的方法,则可以抛出
RuntimeException
或没有声明的子类

class A {
    public void run() {

    }
}

class B extends A {
    @Override
    public void run() {
        throw new RuntimeException("throw without declaration");
    }
}
发件人:

RuntimeException
及其子类是未检查的异常。 未检查的异常不需要在方法或 构造函数的throws子句,如果可以通过执行 方法或构造函数,并在方法或 构造函数边界


throws
子句是方法签名的一部分,为了重写方法,重写方法必须具有与被重写方法相同的签名

因此,如果类
B
扩展类
A
A
定义:

public void foo (int param) throws SomeException
{
    ....
    if (something)
        throw new SomeException ();
    ....
}
然后
B
只能使用相同的签名覆盖foo:

public void foo (int param) throws SomeException
{
    super.foo(param);
    ....
    if (something)
        throw new SomeOtherException ();
    ....
}
SomeOtherException
必须是
SomeException
的子类,因为这是方法签名允许抛出的异常类型。如果重写方法试图抛出类型为
SomeException
的超类的异常,则会违反该方法的约定

让我们声明一些异常类的类层次结构:

SuperException
    SomeException
        SomeOtherException
    AnotherSomeException
您所建议的(
Java允许父类方法检查异常,它是子类方法检查异常的子类
)将允许子类中的重写方法抛出任何类型为
SuperException
的异常,这将允许它抛出另一个
异常
。这违反了方法的签名,因为
另一个SomeException
不是
SomeException
的子类

来扩展我的评论,让我们考虑一下如果java允许你提出什么会发生什么。 假设类

C
有一个方法
bar
,该方法接受类
a
的实例,并调用该方法
foo

public class C
{
    ....
    public void bar (A a)
    {
        try {
            a.foo ();
        }
        catch (SomeException ex) {
            ....
        }
    }
}

由于
foo
被声明为抛出
SomeException
,因此调用
foo
的任何代码都必须处理此异常或声明它可能抛出此异常。现在,如果Java允许
B
重写
foo
,但将其声明更改为
public void foo()抛出异常
,那么
B
foo
的实现可能抛出异常的任何子类。由于
bar
甚至不知道
B
存在,因此它不知道必须捕获除
SomeException
及其子类以外的任何异常。因此,编译器不能将此代码标记为错误,但如果
bar
的调用者将
B
的实例传递给它,则该代码仅在运行时无效,因为它可能抛出一个既不被
bar
捕获也不声明的已检查的expetion。这就是为什么Java不允许您在重写方法时更改
throws
子句的原因。

我想您是在问为什么我不能这样做:

class Parent {
    void doStuff() throws FileNotFoundException {

    }
}

class Child extends Parent {
    @Override
    void doStuff() throws IOException {

    }
}
这很简单,当我这样做时会发生什么:

final Parent parent = new Child();

try {
    parent.doStuff();
} catch (FileNotFoundException e) {
    e.printStackTrace();
}
然后孩子决定扔一个,比如说

一、 正确地,尝试从调用
parent.doStuff
捕获FileNotFoundException,但这不会捕获
SocketException

我如何有一个未捕获的检查异常。这是不允许的

如果我特别尝试捕获SocketException,这将导致编译器错误,因为
SocketException
未声明为从父.doStuff抛出


在一般情况下解决此问题的唯一方法是始终
捕获异常
,因为任何子类都可以声明它抛出更一般的类型并绕过my
捕获
。这显然不理想。

如果未在父类中指定已检查的异常,则重写方法无法引发该异常。原因是,您希望能够在可以传递超类型的任何位置使用子类型的实例,即,您的子类型需要以与超类型相同的方式行为。但是,如果您的子类型可以引发异常,则它的行为方式不同:它可以引发异常。对于未检查的异常也是如此,但是,正如名称已经告诉我们的那样,编译器没有检查它们,因此这只是没有静态增强

我建议你看一看这本书

关于为什么子类在超类承诺不抛出异常的情况下抛出异常是不好的示例:

class Animal {
  void eatMeat() {System.out.println("eating meat");}
}

class Zebra extends Animal {
  void eatMeat() throws IsVegetarianException {throw new IsVegetarianException(); }
}  

嗯。。。也许你的
斑马
不是动物,或者
动物
的界面太宽了。也许不是所有的动物都吃肉。顺便说一下:这发生在各种Java集合接口中。他们通过抛出未检查的
NotSupportedException
来解决这个问题,但这只是设计中的一个缺陷,除了更改设计之外,没有其他好的解决方案

我可能不正确,但我想你是在问为什么超类A不能扩展扩展类A的子类B

这不可能存在,因为不清楚哪一个是父类。

Beca
class A {

    public void doStuff() throws Exception  {

    }
}

class B extends A {

    public void doStuff() throws SQLException {

    }
}