【转载】JAVA:动态绑定 和 静态绑定
通过虚拟机再看Java的静态绑定和动态绑定
动态绑定(auto binding):也叫后期绑定,在运行时,虚拟机根据具体对象的类型进行绑定,或者说是只有对象在虚拟机中创建了之后,才能确定方法属于哪一个对象。
不知道朋友有没有和我一样的疑问,动态绑定的意义是什么呢?
相信大家都知道,java的三大特性:封装,继承和多态,动态绑定就和多态有关。
由于继承和重写的存在,当方法中的类型为父类的时候,编译的时候不太好判断,方法到底要和哪个类绑定,也就是调用的方法依赖于隐式参数的实际类型。
这么说可能比较难理解,上代码,首先创建一个父类Animal,包含一个countLeg()方法。
再创建子类Dog,继承自Animal,重写countLeg()方法。
最后创建一个测试类,调用的方法传递父类Animal作为参数。
这个时候,对于countLeg()方法,是不是就不太好判断,animal到底该绑定哪个对象了!
既然我们在学习虚拟机,也学习了Class文件的知识,那我们就通过Class文件来一窥其妙。
首先,我们看一下Class文件里面的内容,笔者用的是IDEA,借助一下bytecode一下插件,看一下方法内的信息。
可以看到当前的引用类型还是指向Animal的,再来看一下方法中的指令,如下图所示。
1. aload:将指定的引用类型本地变量推送至栈顶。
这个好理解,那就是将animal推送至栈顶。
2. invokevirtual:用于调用对象的实例方法。
这个就是关键了,虚拟机会根据实际类型进行转换。
那么,转换的过程是什么样的呢?
当虚拟机创建Dog类的时候,会创建一个类的方法列表,同时包含父类的方法列表。
同时虚拟机会参数引用的实际地址,找到创建的这个Dog对象,查询方法列表,如果在Dog对象中找到这个方法,就直接调用。
如果没找到,就去查询父类方法,调用父类的方法。
静态绑定(static binding):也叫前期绑定,在程序执行前,该方法就能够确定所在的类,此时由编译器或其它连接程序实现。
那么什么样的方法调用是静态绑定呢?第一个,静态(static)方法是跑不了的,谜底就在谜面上啊,我的天啊,就是这么神奇!
还有一种让方法不被修改的方式,就是使用final进行修饰。
另外,使用private修饰的方法,默认就是final的的(现在已经很少使用final方法了)。
最后一个属于静态绑定的就是构造方法。
上一个小例子简单看一眼。
我们看一下编译之后的指令,test1()使用的是invokestatic指令,test3()使用的是invokespecial指令,test4()使用的是invokevirtual指令,老铁没毛病!