具有对超类型的引用类型的变量可以保存对子类型实例的引用。将子类型实例分配给变量不会以任何方式改变实例;它只是扩大了参考的类型。所有实例数据仍然存在,并且可以通过向下转换来检索。那是,
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!
你会得到 aClassCastException
因为在程序中,p
现在是对Teacher
不是 a 的对象(的实例)的引用Student
。
这是一个类比。用英语,你可能会说,“我有一只宠物”。这并没有说明动物的类型。(例如,您无法知道宠物是否吠叫。)另一方面,如果您说“我有一只宠物狗”,那么询问它是否对陌生人吠叫是有意义的。关键点是:称你的宠物为动物并不会改变它是(或不是)狗的事实。Java 引用的情况完全相同——使用更通用的(例如,基类)引用类型来存储对象引用不会改变对象本身的性质。你失去了对物体细节的了解,但那些细节仍然存在。
重复我在评论中提出的观点:您永远不会将对象分配给 Java 中的变量;您只分配对对象的引用。或者,换句话说,你的变量都不是对象。