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块。
谁能告诉我原因?
答案 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 principle和Design by Contract有些相关,前者(感谢维基百科)说明了以下原则(3和4与你在java代码中观察到的直接相关): / p>
- 子类型中方法参数的反演。
- 子类型中返回类型的协方差。
- 除了子类的方法之外,不应该抛出新的异常 这些例外本身就是哪里 由...抛出的异常的子类型 超类型的方法。
- 无法在子类型中加强先决条件。
- 后置条件不能在子类型中被削弱。
- 超类型的不变量必须保留在子类型中。
醇>
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来捕获。