thinking in java 读书笔记(感悟);
页码:P9 (想看书上怎么讲的。自行翻看 P9 页)
写于:2018年3月29日19:22:14
作者:淮左白衣
目录
场景:
public void eat(Animal animal){
System.out.println(animal.eatSometing());
}
....
....
Dog dog = new Dog();
eat(dog);
Cat cat = new Cat();
eat(cat);
一眼就可以看出,这是多态;在多态的时候,我们将子类的类型向上转型为父类,进行类型的泛化,便于我们写出实用性很强的代码(新类型产生,不用修改原有代码);
就像这里 eat 方法,接受一个 Animal类型 ;我们传进去的分别是 dog 和 cat ;但是打印出的结果是明确的,传dog进去,输出狗吃骨头,传cat进去,输出猫吃鱼 ;
但是这里有个问题,不知道大家想过没有:任何类型传进去,都是父类型,但是在调用方法的时候,却是明明确确的调用了子类的方法!或者 编译器是怎么知道,具体该调用哪一个方法的呢?
这就是本文要讲的 前期绑定 和 后期绑定 。
前期绑定
前期绑定是 面向过程 语言的编译器的做法 ;编译器在 编译 面向过程语言的时候,在出现 函数调 用的时候,会产生一个 对具体函数名字的引用 ,这样在程序运行的时候,执行到函数调用的语句,就会发现这里一个 对XXX方法的引用,就会把执行逻辑解析到这个XXX方法的绝对地址上;
但是这一套,到java这就不好使了,编译器在编译代码的时候,知道具体调用哪一个方法吗?编译器不是神,它不可能知道的;
因此,我们需要另谋蹊径,伟大的计算机科学家,想到了 后期绑定 ;
后期绑定
既然编译时期,编译器搞定不了方法调用的问题!那么,就在运行期,做手脚,来搞定这件事;
java是这样来搞定方法调用的:在每一个对象中存储一小段信息,通过这一小段信息,可以计算出该对象的方法地址 ;然后去调用方法 ;
因此上述代码的调用就可以得到理解:虽然传进去的都是 Animal引用,但是引用指向的对象是Dog、Cat对象,这个对象里面保存了信息;比如Dog对象,里面就保存它的eatSomething方法在哪里 ;这样虚拟机就会调用正确的方法了 ;
顺便讲下 编译看左边,运行看右边 的背后是什么意思吧 ;
编译看左边,运行看右边
这句话,好多人都知道的,但是为什么呢
其实这就是后期绑定的原因 ;java的编译器,虽然在编译期间不能为方法调用做些什么,但是也不能啥也不干吧 ;(它要是不干了,虚拟机可能就不开心了,凭啥都是我在做事呀o(╥﹏╥)o) ;
java的编译器在编译期间,会检查这个要调用的方法是否存在,调用的参数是否正确 ;返回的类型是否正确;简单说,就是做一些检查工作 ;
这个检查工作,就是为什么编译看左边;假如父类没有这个方法,编译时过不去的,因为编译器对方法是否存在进行检查;运行看右边,是因为后期绑定,去调用具体的方法了 ;