4

好的..所以,当你有一个类的层次结构时,比如

public class A {...}

和,

public class B extends A {...}

...当您创建对象时,有什么区别:

A object = new A();
A object = new B();
B object = new B();

感谢您的时间。

4

6 回答 6

2
public class A
{
    public void methodA(){}
}
public class B extends A
{
    public void methodB(){}
}

我希望这可以证明差异。

A obj = new A();

a.methodA(); //works

A obj = new B();

obj.methodA(); //works
obj.methodB(); //doesn't work
((B)obj).methodB(); //works

B obj = new B();

obj.methodA(); //works
obj.methodB(); //works
于 2013-10-05T22:28:10.690 回答
2
A object = new A();

您正在创建A instanceA 类型的引用。您只能访问 A 方法/属性和父方法/属性。

A object = new B();

您正在创建 B instanceA 类型的引用。这样object可以以多态方式运行,例如,如果您在 B 中创建object.method()并被method覆盖,那么它将调用此覆盖方法。您必须注意不要违反Liskov 替换原则。您只能访问 A 方法/属性和父方法/属性。当您只需要超类型合同时,这是首选方式。

B object = new B();

您正在创建一个B instance类型的引用变量B。您只能访问 B 方法/属性和父方法/属性。

于 2013-10-05T22:38:44.497 回答
1
A object = new B();

这声明 thatobject将引用类A或其任何子类的对象(当它不是时null)。编译器会将其视为 type 的对象A,因此您只能访问为A(或其超类之一)声明的方法和字段。这也意味着您可以稍后将其分配给属于类或子类的任何其他对象:A

A object1 = new B();
B object2 = new B();

// reassign later
object1 = new A();  // legal
object2 = new A();  // ILLEGAL

class C extends A { ... }
object1 = new C();  // legal
object2 = new C();  // ILLEGAL

所以初始声明声明object为具有 type A。但是它的初始值是一个类型的对象B,这是可以的,因为B它是 的子类A

这应该可以解释您的第二个和第三个示例之间的区别。第一个和第二个之间的区别只是(在运行时)第一个创建一个新的 type 对象,A第二个创建一个新的 type 对象B

于 2013-10-05T22:28:00.947 回答
1
A object = new A();

object类型A(您可以从 访问字段或方法A

A object = new B();

object类型A(您不能从 访问字段或方法B,只能从A

B object = new B();

object类型(您可以从andB访问字段或方法)AB

于 2013-10-05T22:29:16.540 回答
1
A object1 = new A();
A object2 = new B();
B object3 = new B();

object1被声明为对 A 对象的引用。由于 B 类扩展了 A 类,因此可以将其设置为或(new A()new B()将是有效的)。

object2被声明为对 A 对象的引用,但实际上是 B 对象。假设 B 类有一个名为 的方法eatFood()。如果您尝试使用 访问该方法object2.eatFood(),编译器会抛出错误,因为 eatFood 方法仅在 B 类中。即使该对象实际上是一个 B 对象,由于类型声明,编译器仍认为它是一个 A 对象。要访问eatFood 方法,您必须对其进行类型转换:((B)object2).eatFood().

object3只是对 B 对象的引用,实际上是 B 对象。它可以访问 A 方法和 B 方法。

于 2013-10-05T22:29:33.950 回答
1

像一条线

A var = new B();

是两个独立步骤的简写。

A var;         // (1) Make a variable of TYPE A.
var = new B(); // (2) Make an object of CLASS B, that from now on may be 
               // referred to by the variable var.

所以一个变量有一个TYPE,一个对象有一个CLASS。他们经常匹配。变量的类型通常实际上是一个类,尽管不一定。了解变量类型与变量所指对象的类之间的区别很重要。

一个对象通常属于多个类。如果 B 类扩展了 A 类,这意味着 B 类的所有对象也是 A 类的对象。任何类的所有对象也都是类的对象Object。换句话说,当我们说一个物体是 B 时,这比说它是 A 更具体。就像我们说 Yogi 是一样,这比说 Yogi 是动物更具体,因为所有的熊都是动物

因此,如果 A 是 B 扩展的类,则 A 类型的变量确实可以引用 B 类的对象。但是如果你有一个 A 类型的变量,你就不能用它来做特定于 B 类型的对象的事情。例如,假设 A 类有一个方法被调用display(),而 B 类有一个方法被调用explain()。编译器将允许您调用display()A 类型的变量,但不允许您调用explain(). 如果是这样,那么尝试调用explain()一个实际上不是 B 的对象就会有风险,这会失败。

因此,只要有 B 类定义的方法,您就需要一个 B 类型的变量才能调用它们。当然,你也可以使用同一个变量来调用类 A 中定义的方法。那么从某种意义上说,如果类 B 扩展类 A,那么 B 类型的变量比 A 类型的变量更强大——你可以用它做更多的事情。

所以问题出现了——我为什么要写

A var = new B();

什么时候类型 B 的变量会比var本例中的更强大?

简短的回答是它与查看代码的人交流。它说,“是的,我知道这个变量指的是 B,但我实际上只打算使用 A 类提供的方法。这实际上对试图理解你的代码或维护它的人很有帮助。

在某些情况下,它可以对涉及该变量的方法调用产生真正的影响。假设有另一个类 C,它有两个名称相同但签名略有不同的方法,就像这样。

public class C {
    public void process(A arg){
        // Do some stuff
    }

    public void process(B arg){
        // Do some other stuff
    }
}

在这种特殊情况下,process调用的版本取决于变量的类型,而不是对象的类。所以如果你写

C processor = new C();
A var = new B();
processor.process(var);

这将调用process签名中带有 A 的第一个版本。因为变量的类型。但是如果你写

C processor = new C();
B var = new B();
processor.process(var);

这将调用process签名中带有 B 的第二个版本。

于 2013-10-05T23:55:39.300 回答