Java 为什么可以';重写方法抛出的异常是否比被重写的方法更广泛?

Java 为什么可以';重写方法抛出的异常是否比被重写的方法更广泛?,java,Java,我在阅读Kathe sierra的SCJP 6书时,遇到了关于在重写方法中抛出异常的解释。我完全不明白。谁能给我解释一下吗 重写方法不能抛出新的已检查异常 或比重写方法声明的范围更广。例如,一个 声明FileNotFoundException的方法不能被 方法,该方法声明SQLException、Exception或任何其他非运行时 异常,除非它是FileNotFoundException的子类 这意味着,如果一个方法声明抛出给定的异常,子类中的重写方法只能声明抛出该异常或其子类。例如: cla

我在阅读Kathe sierra的SCJP 6书时,遇到了关于在重写方法中抛出异常的解释。我完全不明白。谁能给我解释一下吗


重写方法不能抛出新的已检查异常 或比重写方法声明的范围更广。例如,一个 声明FileNotFoundException的方法不能被 方法,该方法声明SQLException、Exception或任何其他非运行时 异常,除非它是FileNotFoundException的子类


这意味着,如果一个方法声明抛出给定的异常,子类中的重写方法只能声明抛出该异常或其子类。例如:

class A {
   public void foo() throws IOException {..}
}

class B extends A {
   @Override
   public void foo() throws SocketException {..} // allowed

   @Override
   public void foo() throws SQLException {..} // NOT allowed
}
SocketException扩展了IOException
,但
SQLException
没有扩展

这是因为多态性:

A a = new B();
try {
    a.foo();
} catch (IOException ex) {
    // forced to catch this by the compiler
}
如果
B
决定抛出
SQLException
,那么编译器无法强制您捕获它,因为您正在通过其超类-
A
引用
B
的实例。另一方面,
IOException
的任何子类都将由处理
IOException

您需要能够通过对象的超类引用对象的规则是


由于未经检查的异常可以在任何地方抛出,因此它们不受此规则的约束。如果需要,可以将未经检查的异常作为文档形式添加到throws子句中,但编译器不会强制执行任何相关内容

java.lang.Exception扩展了java.lang.Throwable。java.io.FileNotFoundException扩展了java.lang.Exception。因此,如果一个方法抛出java.io.FileNotFoundException,那么在覆盖方法中,您不能抛出比FileNotFoundException更高的层次结构,例如,您不能抛出java.lang.Exception。不过,您可以抛出FileNotFoundException的子类。但是,您将被迫在重写的方法中处理FileNotFoundException。编一些代码,试试看


规则就在那里,这样你就不会因为扩大了特殊性而丢失原来的抛出声明,因为多态性意味着你可以调用超类上被重写的方法。

假设你有超类A,方法M1通过E1,类B从方法M2通过M1派生。M2不能抛出与E1不同或不太专业的东西


由于多态性,使用类A的客户机应该能够像对待A一样对待B。如果处理类A的代码正在处理异常E1,正如M1声明的那样,它抛出了这个已检查的异常,但随后抛出了不同类型的异常,该怎么办?如果M1抛出IOException,M2很可能抛出FileNotFoundException,因为它是IOException。A的客户可以毫无问题地处理此问题。如果抛出的异常范围更广,则A的客户端将没有机会知道这一点,因此也没有机会捕获它。

要说明这一点,请考虑:

public interface FileOperation {
  void perform(File file) throws FileNotFoundException;
}

public class OpenOnly implements FileOperation {
  void perform(File file) throws FileNotFoundException {
    FileReader r = new FileReader(file);
  }
}
假设你写下:

public class OpenClose implements FileOperation {
  void perform(File file) throws FileNotFoundException {
    FileReader r = new FileReader(file);
    r.close();
  }
}
这将给您一个编译错误,因为r.close()抛出的IOException比FileNotFoundException更广泛

若要解决此问题,请编写:

public class OpenClose implements FileOperation {
  void perform(File file) throws IOException {
    FileReader r = new FileReader(file);
    r.close();
  }
}
您将得到一个不同的编译错误,因为您正在实现perform(…)操作,但抛出了一个未包含在接口的方法定义中的异常

为什么这很重要?界面的使用者可能有:

FileOperation op = ...;
try {
  op.perform(file);
}
catch (FileNotFoundException x) {
  log(...);
}
如果允许抛出IOException,则客户端代码不再正确


请注意,如果使用未检查的异常,则可以避免此类问题。(我不是建议你做或不做,这是一个哲学问题)

重写方法可以抛出任何未经检查的(运行时)异常,不管 重写的方法声明异常

public static class A 
{
    public void m1()
       throws IOException
    {
        System.out.println("A m1");
    }

}

public static class B 
    extends A
{
    @Override
    public void m1()
        throws IOException
    {
        System.out.println("B m1");
    }
}
public static class A 
{
    public void m2()
       throws Exception
    {
        System.out.println("A m2");
    }

}

public static class B 
    extends A
{
    @Override
    public void m2()
        throws IOException
    {
        System.out.println("B m2");
    }
}
    public class Parent {
       public void name()  throws IOException {
           System.out.println(" this is parent");
       }
}
public class Child  extends Parent{
     public  void name() throws IOException{
             System.out.println(" child ");
     }

     public static void main(String[] args) {
        Parent parent  = new Child();

        try {
            parent.name();// output=> child
        }catch( Exception e) {
            System.out.println(e);
        }

    }
}
例如:

class Super {
    public void test() {
        System.out.println("Super.test()");
    }
}

class Sub extends Super {
    @Override
    public void test() throws IndexOutOfBoundsException {
        // Method can throw any Unchecked Exception
        System.out.println("Sub.test()");
    }
}

class Sub2 extends Sub {
    @Override
    public void test() throws ArrayIndexOutOfBoundsException {
        // Any Unchecked Exception
        System.out.println("Sub2.test()");
    }
}

class Sub3 extends Sub2 {
    @Override
    public void test() {
        // Any Unchecked Exception or no exception
        System.out.println("Sub3.test()");
    }
}

class Sub4 extends Sub2 {
    @Override
    public void test() throws AssertionError {
        // Unchecked Exception IS-A RuntimeException or IS-A Error
        System.out.println("Sub4.test()");
    }
}
class Super {
    public void throwCheckedExceptionMethod() throws IOException {
        FileReader r = new FileReader(new File("aFile.txt"));
        r.close();
    }
}

class Sub extends Super {    
    @Override
    public void throwCheckedExceptionMethod() throws FileNotFoundException {
        // FileNotFoundException extends IOException
        FileReader r = new FileReader(new File("afile.txt"));
        try {
            // close() method throws IOException (that is unhandled)
            r.close();
        } catch (IOException e) {
        }
    }
}

class Sub2 extends Sub {
    @Override
    public void throwCheckedExceptionMethod() {
        // Overriding method can throw no exception
    }
}

重写方法不能抛出新的或比那些更广泛的已检查异常 由重写的方法声明

例如:

class Super {
    public void test() {
        System.out.println("Super.test()");
    }
}

class Sub extends Super {
    @Override
    public void test() throws IndexOutOfBoundsException {
        // Method can throw any Unchecked Exception
        System.out.println("Sub.test()");
    }
}

class Sub2 extends Sub {
    @Override
    public void test() throws ArrayIndexOutOfBoundsException {
        // Any Unchecked Exception
        System.out.println("Sub2.test()");
    }
}

class Sub3 extends Sub2 {
    @Override
    public void test() {
        // Any Unchecked Exception or no exception
        System.out.println("Sub3.test()");
    }
}

class Sub4 extends Sub2 {
    @Override
    public void test() throws AssertionError {
        // Unchecked Exception IS-A RuntimeException or IS-A Error
        System.out.println("Sub4.test()");
    }
}
class Super {
    public void throwCheckedExceptionMethod() throws IOException {
        FileReader r = new FileReader(new File("aFile.txt"));
        r.close();
    }
}

class Sub extends Super {    
    @Override
    public void throwCheckedExceptionMethod() throws FileNotFoundException {
        // FileNotFoundException extends IOException
        FileReader r = new FileReader(new File("afile.txt"));
        try {
            // close() method throws IOException (that is unhandled)
            r.close();
        } catch (IOException e) {
        }
    }
}

class Sub2 extends Sub {
    @Override
    public void throwCheckedExceptionMethod() {
        // Overriding method can throw no exception
    }
}

我们认为下面的解释是什么

class BaseClass {

    public  void print() {
        System.out.println("In Parent Class , Print Method");
    }

    public static void display() {
        System.out.println("In Parent Class, Display Method");
    }

}


class DerivedClass extends BaseClass {

    public  void print() throws Exception {
        System.out.println("In Derived Class, Print Method");
    }

    public static void display() {
        System.out.println("In Derived Class, Display Method");
    }
}
当print方法引发异常时,类DerivedClass.java引发编译时异常,基类的print()方法不会引发任何异常


我能够将这归因于异常比RuntimeException窄,它可以是无异常(运行时错误)、RuntimeException及其子异常。重写方法不能抛出比重写方法声明的新的或更宽的选中异常。

public static class A 
{   
    public void m3()
       throws IOException
    {
        System.out.println("A m3");
    }
}

public static class B 
    extends A
{   
    @Override
    public void m3()
        //throws NOTHING
    {
        System.out.println("B m3");
    }
}
这仅仅意味着当重写现有方法时,此重载方法引发的异常应该是原始方法引发的相同异常或其任何子类

请注意,检查是否处理了所有已检查的异常是在编译时完成的,而不是在运行时完成的。因此,在编译时,Java编译器检查重写方法引发的异常类型。由于将执行哪个被重写的方法只能在运行时决定,因此我们无法知道必须捕获哪种类型的异常


示例

假设我们有类
A
及其子类
B
A
具有方法
m1
,而class
B
已重写此方法(我们将其称为
m2
,以避免混淆..)。现在让我们假设
m1
抛出
E1
,而
m2
抛出
E2
,这是
E1
的超类。现在我们编写以下代码:

A myAObj = new B();
myAObj.m1();
请注意,
m1
不过是对
m2
的调用(同样,方法签名在重载方法中是相同的,因此不要与
m1
m2
混淆。在本例中,它们只是为了区分……它们是
    public class Parent {
       public void name()  throws NullPointerException {
           System.out.println(" this is parent");
       }
}

public class Child  extends Parent{
     public  void name() throws RuntimeException{
             System.out.println(" child ");
     }

     public static void main(String[] args) {
        Parent parent  = new Child();
        parent.name();// output => child
    }
}
    public class Parent {
       public void name()  throws RuntimeException {
           System.out.println(" this is parent");
       }
}

public class Child  extends Parent{
     public  void name() throws  NullPointerException {
             System.out.println(" child ");
     }

     public static void main(String[] args) {
        Parent parent  = new Child();
        parent.name();// output => child
    }
}
    public class Parent {
       public void name()  throws IOException {
           System.out.println(" this is parent");
       }
}
public class Child  extends Parent{
     public  void name() throws IOException{
             System.out.println(" child ");
     }

     public static void main(String[] args) {
        Parent parent  = new Child();

        try {
            parent.name();// output=> child
        }catch( Exception e) {
            System.out.println(e);
        }

    }
}
import java.io.IOException;

public class Parent {
       public void name()  throws IOException {
           System.out.println(" this is parent");
       }
}
public class Child  extends Parent{
     public  void name() throws Exception{ // broader exception
             System.out.println(" child ");
     }

     public static void main(String[] args) {
        Parent parent  = new Child();

        try {
            parent.name();//output=> Compilation failure
        }catch( Exception e) {
            System.out.println(e);
        }

    }
}
 1. No exception or
 2. Any number of unchecked exception
 3. but strictly no checked exception
 1. No exception or
 2. Any number of unchecked exception 
 3. but strictly no checked exception
 1. No exception or
 2. Same checked exception or
 3. Sub-type of checked exception or
 4. any number of unchecked exception