好。所以, 当你拥有类如对象类型声明
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();
谢谢你的时间。
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
的新对象。
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
类型的object
(你可以访问从A
字段或方法)
A object = new B();
A
类型的object
(你不能从B
访问场或方法中,只有从A
)
B object = new B();
object
B
类型(可以从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方法。
我相信应该是'((B)object2).eatFood()',因为演员操作符的优先级...我自己一直在遇到这个问题。 – ajb
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方法/属性和父项方法/属性。
像
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的签名。
当您创建('new')一个A时,您创建了一个A.当您创建一个B时,您创建了一个B.但B包含了A的所有功能,并且在大多数情况下可以像A一样使用。如果在一个带有“A”类型的变量中引用B,则不能访问该对象的“B-ness”,而只能访问“A-ness”。但是,您可以将参考“转换”回“B”以重新获得它的“B-ness”:'B refB =(B)refA;'但演员('(B)')在运行时会失败如果refA实际上没有引用B. –