class Person {
public String toString(){
return "I'm a person.";
}
public void eat(){}
public void speak(){}
}
class Boy extends Person{
public String toString(){
return "I'm a boy";
}
public void speak(){}
public void fight(){}
}
class Girl extends Person{
public String toString(){
return "I'm a girl";
}
public void speak(){}
public void sing(){}
}
//最后这么用的话,
Person girl = new Girl();
girl.speak();
girl
引用变量指向堆中的 Girl
对象,所以运行的时候知道当前对象是Gir
l对象,找Girl
的方法表,找到后执行。这对于speak
方法来说是行得通的。但是如果这样呢:girl.sing()
;去Girl
方法表找到sing
方法,然后执行。
实际情况大家都知道有错误,编译不通过。当发生向上转型的时候,栈和堆中分别发生了什么?
是变量的声明类型决定了它能调用哪些方法,而不是它指向的堆中的对象的实际类型。
像你这样:
Person girl = new Girl();
girl.speak();
girl
的声明类型是Person
,而Person
并没有speak
这个方法,所以会报错。注意是编译器报的编译期的错误,编译器是不管你那个girl
执行的类型到底是什么。
运行期,虚拟机当然会知道girl执行的堆中对象是Girl
类型。
编译器为什么要根据变量的声明类型来决定能不能调用某个方法?其中一个原因就是,编译期间可能根本不知道这个变量在运行期绑定的到底是什么类型的对象。举个例子:Person p = Math. random() > 0.5 ? new Girl() : Boy();
这个例子中的p不到运行时根本没法确定它绑定的对象的真实类型。
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。