好的..所以, 当你有一个类的层次结构,如
public class A {...}
和
public class B extends A {...}
...创建对象时,有什么区别:
A object = new A();
A object = new B();
B object = new B();
感谢您的时间。
答案 0 :(得分: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 :(得分:2)
A object = new A();
您正在类型A的引用中创建A instance
。您可以只访问方法/属性和父方法/属性。
A object = new B();
您正在A类型的引用中创建B instance
。这样object
可能会以多态方式运行,例如,如果您object.method()
和method
是{在B中重写然后它将调用此覆盖方法。你必须注意不要破坏Liskov Substitution Principle。您可以只访问方法/属性和父方法/属性。当您只需要超类型合约时,这是首选方式。
B object = new B();
您正在B instance
类型的引用变量中创建B
。您可以只访问B方法/属性和父方法/属性。
答案 2 :(得分: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
类型的对象,这是正常的,因为B
是A
的子类。
这应该解释你的第二个和第三个例子之间的区别。第一个和第二个之间的区别仅在于(在运行时)第一个创建类型为A
的新对象,第二个创建类型为B
的新对象。
答案 3 :(得分:1)
A object = new A();
类型为object
的 A
(您可以访问A
中的字段或方法)
A object = new B();
类型为object
的 A
(您无法访问B
中的字段或方法,仅来自A
)
B object = new B();
类型object
的 B
(您可以访问A
和B
中的字段或方法)
答案 4 :(得分: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方法。
答案 5 :(得分: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是一个< em> animal ,因为所有熊都是动物。
因此,如果A是B扩展的类,则类型A的变量确实可以引用类B的对象。但是如果你有一个A类型的变量,就不能用它来做特定于B类对象的事情。例如,假设类A有一个名为display()
的方法,而类B有一个名为explain()
的方法。编译器允许您对类型A的变量调用display()
,但不允许您调用explain()
。如果确实如此,那么尝试在一个实际上不是B的对象上调用explain()
会有失败的风险。
因此,只要有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的那个版本。