x2
编译时类型为X
,运行时类型为Y
. 这意味着,当编译器需要推理时,x2
它会假设它x2
是一个X
. 但是在运行时, 的行为x2
将是 a的行为Y
。
所以让我们更详细地解释一下。这是不合法的:
x2.do2();
这是因为编译器认为那x2
是一个X
并且X
没有一个名为的方法do2
,只有Y
。编译器不知道那x2
是 a Y
,它只知道编译时类型x2
是 a X
。
但是,这将是合法的,并且不会导致运行时异常:
((Y)x2).do2();
我们告诉编译器,看,我知道的比你多x2
;我知道它是一个Y
,所以只需发出调用Y.do2
withx2
作为接收器的指令。
此外,假设我们有一个接受Y
s 的方法:
void M(Y y) { }
那么这将是不合法的:
M(x2);
同样,这是因为编译器认为它x2
是 a X
,不是所有X
的 s 都是Y
s,所以它必须拒绝方法调用。
但是,这将是合法的,并且不会导致运行时异常:
M((Y)x2);
再一次,我们告诉编译器,看,我知道的比你知道的多x2
;我知道它是 a ,所以请相信我,并像aY
一样调用该方法。x2
Y
让我们进一步假设我们有一个在 中定义X
并被覆盖的方法Y
:
class X {
void do1() {}
void N() { System.out.println("X");
}
class Y extends X { void do2() {}
@Override
void N() { System.out.println("Y");
}
现在如果我们说:
x2.N();
我们将看到Y
打印到控制台。这是因为运行时类型x2
是Y
.
所有这些都是人们在谈到多态性时所要表达的意思的一部分。
使用两个构造函数创建对象是否合法。
此语句中没有两个构造函数:
X x2 = new Y();
只有一个构造函数。左侧是变量声明。它声明了一个名为x2
type的变量X
。右侧是构造函数调用。我们正在调用 ; 的公共无参数构造函数Y
。这将创建一个新的Y
. 整个语句是一个赋值语句。我们将右侧的结果分配给左侧的变量。赋值是合法的,因为所有Y
的 s 也是多态X
的,因为Y extends X
.