1

这是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:XY?

4

6 回答 6

3

那些不是两个构造函数

 X x2 = new Y(); 
  • X是变量的类型
  • x2是可以引用的引用变量,因此可以引用 的X任何子类X,在这种情况下,它可以引用Y.
  • new Y()实际上Y在内存中创建类的对象,变量x引用该对象。

这是可能的,因为YextendsX因此它通过了IS-A测试。这是多态性的例子。超类型是指子类型的对象。

  • 在编译时,x2引用变量的类型为X
  • 在运行时,x2将引用一个子类对象,即 type Y

因此,如果类X中的某个方法被类中的方法覆盖,则将调用类Y的方法,Y因为该对象是类型的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();创建对象。XXsomeMethodX
  • 该语句X x2 = new Y();创建了X类的对象的引用变量,Y因此调用了类的覆盖方法,Y因为覆盖的方法具有动态绑定。调用哪个重写方法取决于对象类型。
  • 对于语句Y y = new Y(),与第 1 点的解释相同。
于 2013-08-02T13:15:03.203 回答
1

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.do2withx2作为接收器的指令。

此外,假设我们有一个接受Ys 的方法:

void M(Y y) { }

那么这将是合法的:

M(x2);

同样,这是因为编译器认为它x2是 a X,不是所有X的 s 都是Ys,所以它必须拒绝方法调用。

但是,这将是合法的,并且不会导致运行时异常:

M((Y)x2);

再一次,我们告诉编译器,看,我知道的比你知道的多x2;我知道它是 a ,所以请相信我,并像aY一样调用该方法。x2Y

让我们进一步假设我们有一个在 中定义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打印到控制台。这是因为运行时类型x2Y.

所有这些都是人们在谈到多态性时所要表达的意思的一部分

使用两个构造函数创建对象是否合法。

此语句中没有两个构造函数:

X x2 = new Y();

只有一个构造函数。左侧是变量声明。它声明了一个名为x2type的变量X。右侧是构造函数调用。我们正在调用 ; 的公共无参数构造函数Y。这将创建一个新的Y. 整个语句是一个赋值语句。我们将右侧的结果分配给左侧的变量。赋值是合法的,因为所有Y的 s 也是多X的,因为Y extends X.

于 2013-08-02T13:15:19.623 回答
0

使用使用另一个构造函数的构造函数创建对象是合法的。

于 2013-08-02T13:19:51.930 回答
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 中调用它们。

于 2013-08-02T13:17:17.620 回答
0

是的,它是合法的,它不是两个构造函数,只是不同的引用类型。

实例是Y类型,但引用是X类型。所以你将无法调用它的方法Y

new Y()创建 Y aka Runtime类型的新实例

X x2 = new Y();将其分配给可以保存 Y 的类型 X(又名编译时类型)的引用,因为 X 是 Y 的超类

去测试:

if(x2 instanceof Y){
   System.out.println("Instance is of Y");
}
于 2013-08-02T13:15:03.583 回答
0

x2实际上是Y运行时的对象。在编译时,x2将被视为X对象。

这对于多态性非常有用,在编译时,您可能并不真正知道您将处理什么类型的对象,但您知道您将处理继承自X.

于 2013-08-02T13:15:42.723 回答