好的..所以,当你有一个类的层次结构时,比如
public class A {...}
和,
public class B extends A {...}
...当您创建对象时,有什么区别:
A object = new A();
A object = new B();
B object = new B();
感谢您的时间。
好的..所以,当你有一个类的层次结构时,比如
public class A {...}
和,
public class B extends A {...}
...当您创建对象时,有什么区别:
A object = new A();
A object = new B();
B object = new B();
感谢您的时间。
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
A object = new A();
您正在创建A instance
A 类型的引用。您只能访问 A 方法/属性和父方法/属性。
A object = new B();
您正在创建 B instance
A 类型的引用。这样object
可以以多态方式运行,例如,如果您在 B 中创建object.method()
并被method
覆盖,那么它将调用此覆盖方法。您必须注意不要违反Liskov 替换原则。您只能访问 A 方法/属性和父方法/属性。当您只需要超类型合同时,这是首选方式。
B object = new B();
您正在创建一个B instance
类型的引用变量B
。您只能访问 B 方法/属性和父方法/属性。
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
。
A object = new A();
object
类型A
(您可以从 访问字段或方法A
)
A object = new B();
object
类型A
(您不能从 访问字段或方法B
,只能从A
)
B object = new B();
object
类型(您可以从andB
访问字段或方法)A
B
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 方法。
像一条线
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 的第二个版本。