我一定错过了继承中的基本内容,因为它对我没有意义。
鉴于这两个类:
class Person
{
private String name;
}
class Student extends Person
{
private String degree;
}
为什么可以创建新的Student
对象,将其存储在Person
类型变量中,并且仍然可以保留变量String degree
并且可以访问它?
Person p = new Student("John", "Master's");
我认为可以使用上述初始化,因为p
除了name
之外不会有任何变量,因为Person
变量不能包含degree
变量,因此它是合法的 - 但是当你这样做时,degree
中不存在p
。
这就是我认为原因在于相反的事实是违法的
(Student s = new Person("John");
) - 因为s
不会将其他变量(degree
)初始化。
答案 0 :(得分:3)
具有超类型引用类型的变量可以保存对子类型实例的引用。将子类型实例分配给变量 not 以任何方式更改实例;它只是拓宽了参考的类型。所有实例数据仍然存在,并且可以通过向下转换来检索。也就是说,
Student s = new Student("John", "Master's");
Person p = s; // no cast needed for a widening conversion
与:
完全相同Person p = new Student("John", "Master's");
Student s = (Student) p; // cast required for a narrowing conversion
编辑也许这有助于澄清事情。
假设有Person
的第二个子类:
class Teacher extends Person
{
private String department;
}
现在你可以这样做:
Person p = new Teacher("Fred", "Physics");
Teacher t = (Teacher) p;
但如果你后来试图这样做:
p = new Teacher("Fred", "Physics");
Student s = (Student) p; // Error!
你会得到一个ClassCastException
,因为在程序的那一点上,p
现在是一个对象(Teacher
的一个实例)的引用,它不是{{1} }}
这是一个类比。在英语中,你可能会说,“我有一只宠物动物”。这对动物的类型一无所知。 (例如,你无法知道宠物是否会吠叫。)另一方面,如果你说“我有一只宠物狗”,那么问它是否会陌生人吠叫是有意义的。关键点是:将宠物称为动物不会改变它(或不是)狗的事实。 Java引用完全相同 - 使用更通用(例如,基类)引用类型来存储对象引用不会改变对象本身的性质。您丢失了知识对象的详细信息,但这些细节仍然存在。
重复我在评论中提出的观点:你永远不会将对象分配给Java中的变量;您只将引用分配给对象。或者,换句话说,你的变量都不是对象。
答案 1 :(得分:0)
变量不持有对象。变量包含引用,标识对象。如果有一个汽车识别号码列表(美国的VIN),列表本身可能包含许多不同种类的汽车的号码;从列表的角度来看,每个数字都可以识别车辆,但是一个号码可以识别FordFocus,另一个号码可以识别ToyotaPrius,另一个号码可以识别ChevroletSebringConvertible等。如果一个人在列表上写下ChevroletSebringConvertible的号码,列表将没有认为这是一辆敞篷车,因此无法在没有首先确定第五辆VIN识别的汽车有敞篷车顶部的情况下,不能说“在列表上的第五辆车上升”。当汽车被确定为敞篷车时,顶部不会出现;无论谁知道它们,顶部及其状态(升高或降低)都将继续存在。