2013-10-05 149 views
4

好。所以, 当你拥有类如对象类型声明

public class A {...} 

和层次结构,

public class B extends A {...} 

...当你创建对象,是有什么区别:

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

谢谢你的时间。

+0

当您创建('new')一个A时,您创建了一个A.当您创建一个B时,您创建了一个B.但B包含了A的所有功能,并且在大多数情况下可以像A一样使用。如果在一个带有“A”类型的变量中引用B,则不能访问该对象的“B-ness”,而只能访问“A-ness”。但是,您可以将参考“转换”回“B”以重新获得它的“B-ness”:'B refB =(B)refA;'但演员('(B)')在运行时会失败如果refA实际上没有引用B. –

回答

1
A object = new B(); 

此声明object将指A类的对象或其任何亚类的(当它不是null)。编译器会将它视为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具有类型A。但其初始值是B类型的对象,这是可以的,因为BA的子类。

这应该解释你的第二个和第三个例子的区别。第一个和第二个之间的区别仅仅是(在运行时)第一个创建了一个类型为A的新对象,第二个创建了一个类型为B的新对象。

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 
1
A object = new A(); 

A类型的object(你可以访问从A字段或方法)

A object = new B(); 

A类型的object(你不能从B访问场或方法中,只有从A

B object = new B(); 

objectB类型(可以从AB访问场或方法)

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方法。

+1

我相信应该是'((B)object2).eatFood()',因为演员操作符的优先级...我自己一直在遇到这个问题。 – ajb

2
A object = new A(); 

您在类型A的引用中创建A instance。您可以仅访问方法/属性和父项方法/属性。

A object = new B(); 

你正在创造型A的参考B instance这样object可能表现在多态的方式,例如,如果你让object.method()method在B重写,然后它会调用这个重写方法。你必须小心不要打破Liskov Substitution Principle。您只能访问A方法/属性和父项方法/属性。当您只需要超类型合同时,这是首选方式。

B object = new B(); 

你正在创建在B类型的参考变量B instance。您只能访问B方法/属性和父项方法/属性。

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更具体。就像当我们说瑜珈是一个时,这比说瑜珈是动物更具体,因为所有动物

因此,A类型的变量确实可以引用类B的对象,如果A是B延伸的类。但是如果你有一个A类型的变量,你不能用它来做特定于B类对象的事情。例如,假设A类有一个叫display()的方法,而B类有一个叫做explain()的方法。编译器会让你在A类型的变量上调用display(),但它不会让你调用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的签名。