首先我定义了超类:
public class Living {
double energy;
Living(double energy) {
this.energy = energy;
}
}
然后我定义了子类:
public class Person extends Living {
String name;
Person(double energy, String name) {
super(energy);
this.name = name;
}
public String getName() {
return this.name;
}
public String toString() {
String s = this.getName() + " " + this.energy;
return s;
}
}
现在,如果我这样做:
Living creature = new Person(15.2, "Joe");
我将使用Living
构造函数创建一个Person
类型的生物。这是允许的,因为Person
是Living
的特化。
但是我仍然无法通过点运算符访问Person
类的方法 - 例如creature.getName()
,因为生物被声明为Living
类型而不是Person
System.out.println(creature.getName()); //not possible
但如果我这样做:
System.out.println(creature.toString());
我得到了
Joe 15.2
因此,不是toString()
类的Living
方法(不覆盖),而是调用toString()
类的Person
方法。我无法理解为什么会这样。
我在开头声明生物应该是Living
类型,尽管我使用Person
构造函数。如果生物没有name
属性且其toString()
方法未被覆盖,我该如何获得上述输出?
答案 0 :(得分:3)
该对象确实是Person
的一个实例,因此具有所有Person
方法。但是,引用属于Living
类型。编译器不知道它的运行时类型,只允许你访问它已经知道的方法。将是可用的 - 即Living
类型的方法。
然而,可以强制将此变量视为Person
,但显式转换:
System.out.println(((Person) creature).getName())
答案 1 :(得分:2)
对于每个对象,都有一个表(虚拟表),用于保存有关为此对象定义的方法的信息。
如果在给定对象中重写了父方法,则此表将存储重写方法的地址,即使该对象被引用为父类之一。每次调用方法时,此表用于查找如何访问该方法。
因此,在您的情况下,它将使用重新定义的方法toString,因为它已替换父方法。
答案 2 :(得分:1)
Living
在Object中声明(您的所有类都继承了该数据),这就是为什么您可以调用它而不在类equals()
中重写(有些适用于hashCode()
,toString()
等方法。
请注意,如果您要在Living
的其他子类上调用Animal
,例如toString()
,不明确覆盖{{1} }},您将从toString()
获得默认的Object
结果。
public class Animal extends Living {
String name;
Person(double energy, String name) {
super(energy);
this.name = name;
}
public String getName() {
return this.name;
}
// no toString override here
}
在toString()
上致电Animal
:
Living animal = new Animal(100, "Fido");
System.out.println(animal.toString()); // something like your.package.Animal@757dbeaf
答案 3 :(得分:1)
这是面向对象编程的主要特征之一。
执行Living creature = new Person(15.2, "Joe");
时,您定义的变量类型为Living
,但如您所述,专门为Person
。这意味着它不会拥有Person
方法,只有它的专业化!
因此,例如,如果Living
有方法
public String getName() {
return "foobar";
}
并且您致电creature.getName()
,回报不是"foobar"
,而是"Joe"
。因为'专业化'将调用该方法,而不是super方法本身。
但是,只有专业的方法才能实现。可以被称为:那些特定于Person
或任何其他子类的人都可以。
答案 4 :(得分:0)
对象的类型为Person
,但类型为Living
时的引用。由于Living
没有声明getName()
方法,因此当您尝试调用它时会出现编译错误。
toString()
方法实际上是在Object
中声明的,Living
是toString()
的隐式父级,因此Person
是< / em>声明。由于对象属于toString()
类型,并且它会覆盖<a href="Your Link Here" target="_blank" onclick="window.open('Your Link Here');">Click Here</a>
,因此它是被调用的对象。