在继承中使用异常

时间:2010-10-27 13:38:48

标签: java

class B {
    void process()throws Exception{
        System.out.println("hi sh");
    }

}
class C extends B {
    void process(){
        System.out.println("hhhhhh");
    }

    public static void main(String args[]){
        B a=new C();
        try{
             a.process();
        }
        catch(Exception e)
        {
        }
    }
}

在调用process方法时,我们必须使用try catch块。但是,如果我只将C的对象存储在C的引用变量中,即C a=new C(),则不需要尝试使用catch块。

谁能告诉我原因?

6 个答案:

答案 0 :(得分:5)

编译器不能(通常)告诉变量a在运行时将具有哪种类型。因此,它(始终)采用安全路线,并要求您在呼叫周围放置try / catch

当你这样做时

C a = new C();

相反,编译器可以确保在运行时不调用B.process(),因此您可以在没有try / catch的情况下调用它

答案 1 :(得分:1)

参考作业将在运行时完成。

异常声明检查在编译时完成。

如果你写

B a=new C();//

这里编译器知道a的方法也可以被调用它是运行时决定因此它迫使我们处理异常。

但是如果你使用

C a=new C() ;

编译器非常确定将调用C的方法,因此它不会声明抛出异常,因此它允许

答案 2 :(得分:0)

因为B.process()的签名是void process() throws Exception

C课程中,您已将签名缩小为void process()

当您声明类型为B的引用时,您需要处理B.process()声明要抛出的(已检查)异常。

当您将引用声明为C类型时,没有声明C.process()被抛出的异常。

将对象实例化为B a = new C()时,a属于运行时类型C,但声明类型为B。编译器将a视为类型B ,因为您将其声明为B 。这是工作中的多态性。

答案 3 :(得分:0)

仅在已检查例外时才会发生这种情况,这是由于多态性造成的。

在您的情况下为C is-a B,因此当您致电process()时,您可能会抛出已检查的异常( 要处理的异常)

最安全的处理异常是强制开发人员在 a B(也可能是C)上调用process()时处理此异常,但之前你不知道运行时)。

答案 4 :(得分:0)

  

这里调用流程方法   我们必须使用try catch块但是   如果我存储C的对象   C的参考变量,即C   a = new C()然后尝试catch块不是   需要。任何人都可以告诉我原因   为什么呢?

您需要记住,引用的run-time类型不一定与declared类型相同。例如:

Number n = new Integer(1);

这声明n是声明类型 java.lang.Number引用的引用,是特定类/类型{{1}的java.lang.Number的子类}}。

有关实际 运行时类型的信息(通常)仅在运行时可用。但编译器仅在编译时运行。在编译时,编译器(通常)只知道声明的类型。

因为它通常只知道声明的类型,所以它只能用于做出可编译和不可编码的决策。也就是说,javac只能在编译时基于它知道(或可以推断)的内容来强制执行规则。

回到你的代码示例:

编译器正在查看引用的声明的类型(在这种情况下,引用java.lang.Integer的类型为a)。基于此,编译器知道方法B抛出了需要强制执行的已检查异常。

即,

B.process()

B a=new C();

声明B a=new B(); 类型为a的{​​{1}}方法抛出已检查的异常。但是,如果您有以下内容:

B

然后,声明的类型为process(),而不是C a = new C(); 。由于子类可以弱化其继承方法的前提条件,因此C可以自由地削弱B的前提条件。也就是说,它可以声明C抛出process()引发的异常集的子集(在C.process()情况下,它是空集。)

这与Liskov substitution principleDesign by Contract有些相关,前者(感谢维基百科)说明了以下原则(3和4与你在java代码中观察到的直接相关): / p>

  
      
  1. 子类型中方法参数的反演。
  2.   
  3. 子类型中返回类型的协方差。
  4.   
  5. 除了子类的方法之外,不应该抛出新的异常   这些例外本身就是哪里   由...抛出的异常的子类型   超类型的方法。
  6.   
  7. 无法在子类型中加强先决条件。
  8.   
  9. 后置条件不能在子类型中被削弱。
  10.   
  11. 超类型的不变量必须保留在子类型中。
  12.   

Java编译器只是试图强制执行其中一些原则,并且出于实际目的,它使用声明的类型的引用变量来确定如何来强制执行它们。

答案 5 :(得分:0)

当我们写:

B a=new C();

我们隐式地将C的对象类型转换为对象B.

a.process();

在运行时,它将调用C的process(),但在编译时,对于编译器a仍然是B的对象,并且因为B有process()抛出异常所以它必须使用try / catch捕获。

如果我们使用:

C a = new C();
a.process();

在编译时,对于编译器来说仍然是C的对象(并且可能包含对其他子类对象的引用),并且因为C的进程()没有抛出任何异常所以不需要使用try / catch来捕获。