这是来自Kathy Sierra的“SCJP” 的示例:第2章 - 面向对象
class X { void do1() {} }
class Y extends X { void do2() {} }
class Chrome {
public static void main (String args[]) {
X x1 = new X();
X x2 = new Y(); //***** question is regarding this line
Y y1 = new Y();
((Y)x2).do2();
}
}
使用两个构造函数创建对象是否合法?什么类型的对象是x2
:X
或Y
?
答案 0 :(得分:3)
这些不是两个构造函数
X x2 = new Y();
X
是变量类型x2
是引用变量,可以引用X
,因此引用X
的任何子类,在这种情况下,它可以引用Y
。new Y()
实际上在内存中创建了类Y
的对象,变量x
引用了该对象。这是可能的,因为Y
扩展了X
因此它通过了IS-A
测试。这是多态性的例子。超类型指的是子类型的对象。
x2
引用变量的类型为X
x2
将引用子类对象,即Y
类型。因此,如果类X
中的某个方法被类Y
中的方法覆盖,则类Y
的方法将被调用,因为该对象的类型为{{ 1}}。
考虑这些课程。
Y
和另一个班级
public class X {
public void someMethod(){
System.out.println("we are in class X method");
}
}
此输出
public class Y extends X {
public void someMethod(){
System.out.println("we are in class Y method ");
}
public static void main(String [] args){
X x = new X();
X x2 = new Y();
Y y = new Y();
x.someMethod();
x2.someMethod();
y.someMethod();
}
}
解释是:
we are in class X method
we are in class Y method
we are in class Y method
参考X x = new X();
创建X
的对象,因此请调用X
类的someMethod
。X
创建X x2 = new Y();
的引用变量,但是类X
的对象,因此调用类Y
的重写方法,因为重写的方法具有动态绑定。以及要调用的重写方法取决于对象类型。Y
,与第1点相同。答案 1 :(得分:1)
x2
的编译时类型为X
,但运行时类型为Y
。这意味着,当编译器需要推理x2
时,它会认为x2
是X
。但在运行时,x2
的行为将是Y
的行为。
因此,让我们更详细地解释一下。这不合法:
x2.do2();
这是因为编译器认为x2
是X
而X
没有名为do2
的方法,只有Y
。编译器不知道x2
是Y
,它只知道x2
的编译时类型是X
。
然而,这是合法的,并且不会导致运行时异常:
((Y)x2).do2();
我们告诉编译器,看,我比x2
知道的更多;我知道它是Y
,因此只需发出以Y.do2
作为接收方调用x2
的指令。
此外,我们假设我们有一个接受Y
s的方法:
void M(Y y) { }
然后这不是合法的:
M(x2);
同样,这是因为编译器认为x2
是X
,而不是所有X
都是Y
,所以它必须拒绝方法调用。
然而,这是合法的,并且不会导致运行时异常:
M((Y)x2);
同样,我们告诉编译器,看,我知道的比x2
更多;我知道它是Y
,所以请相信我,并调用该方法,就像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
。
所有这些都是人们在谈到polymorphism时的意思的部分。
使用两个构造函数创建对象是否合法。
此声明中有不两个构造函数:
X x2 = new Y();
只有一个构造函数。左侧是变量声明。它声明了一个名为x2
的{{1}}类型的变量。右侧是构造函数调用。我们正在调用X
的公共无参数构造函数;这将创建Y
的新实例。整个陈述是一个赋值语句。我们将右侧的结果分配给左侧的变量。该作业是合法的,因为所有Y
多态也是Y
s,因为X
。
答案 2 :(得分:0)
是的,它是合法的,它不是两个构造函数,只是不同的引用类型。
该实例属于Y
类型,但引用属于X
类型。因此,您无法在此处调用Y
的方法。
new Y()
创建了Y又名运行时类型
X x2 = new Y();
将其指定为类型X的引用(又名编译时间类型),它可以容纳Y,因为X是Y的超类
测试:
if(x2 instanceof Y){
System.out.println("Instance is of Y");
}
答案 3 :(得分:0)
x2
在运行时实际上是一个Y
对象。在编译时,x2
将被视为X
对象。
这对多态非常有用,在编译时,你可能不知道你将要处理的对象类型,但是你知道你将处理从X
继承的对象。 / p>
答案 4 :(得分:0)
您始终可以将派生类分配给类层次结构中的类型变量。
X x = new Y(); // is valid.
Y y = x; // is not valid without a cast, even though x is actually of class Y
因此,将Y的对象分配给X类型的变量是有效的,但不是相反。
如果你在X上调用函数并在Y中覆盖它们,那么它们也会被调用。但是,如果Y引入新功能,当然不能从X调用它们。
答案 5 :(得分:0)
使用使用另一个构造函数的构造函数创建对象是合法的。