从JVM角度看为什么子类不能重写父类静态方法
来源:互联网 发布:网络用语蛤蟆啥意思 编辑:程序博客网 时间:2024/06/03 17:03
划重点:本文的概念知识来自《深入理解JVM虚拟机》[周志明 著]
我们要解释的是什么问题呢?
public class A extends B{ public static void f() { System.out.println("com.sdkd.A.f()"); } public static void main(String[] args) { A.f(); }}class B { public static void f() { System.out.println("com.sdkd.B.f()"); }} /**com.sdkd.A.f()*/
我在网上搜这个问题的时候答案多数是这样的
- 子类不能重写实例方法
- 父类的静态方法被绑定了[写死了orz…],不能重写
好了,开始我们的分析
调用A.f()
发生了什么呢?
注意这里是“调用”而不是“执行”,方法调用就是确定被调用方法的版本[即调用哪一个方法]。
在JAVA虚拟机中提供了5中方法调用字节码指令,如下
- invokestatic, 调用静态方法
- invokespecial, 调用实例构造器init方法、私有方法和父类方法
- invokevirtual, 调用所有的虚方法[public]
- invokeinterface , 调用接口方法,会在运行时再确定一个实现此接口的对象
- invokedynamic, 先在运行时动态解析出调用点限定符所引用的方法,然后再执行该方法,在此之前的4条调用指令,分派逻辑是固化在Java虚拟机内部的,而invokedynamic指令是由引导方法决定的.
先普及一点知识点:
符号引用:符号引用是一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。符号引用与虚拟机内部的内存布局无关,引用的目标并不一定加载到内存中。各种虚拟机实现的内存布局可以各不相同,但是他们能接受的符号引用是相同的,因为符号引用的字面量形式明确定义在虚拟机规范的Class文件规范中
比如
public class C { public void f(){ }}
使用javap -v C.class,我们看该class文件中的常量池中f()
的符号引用——#11
直接引用:直接引用可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。直接引用是和虚拟机内存布局相关的,同一个符号引用在不同虚拟机实例上翻译出来的直接引用一般不会相同。如果有了直接引用,那引用的目标必定已经在内存中存在
比如
public class C { public void f(){ }}
直接引用是我们是看不到的,但是如果我们用偏移量来表示f()
,它可以是0x00000045
[偏移量].
首先我们知道普通的public方法是能够被重写的,它在class方法中的字节码指令是invokevirtual,有了指令也要有参数——方法的入口地址,类似于这种invokevirtual address
。该入口地址是要动态解析的,也就是将方法引用解析为直接引用,类似于这种invokevirtual 0xffffff
举例
public class C { public void f(){ }}class D extends C { public void f() { } public static void main(String[] args) { C c = new D(); c.f(); }}
运行时执行这里的invokevirtual指令时,将符号引用f解析成一个具体的直接引用0xfffffff
解析过程:
- 类方法和接口方法引用的常量类型定义是分开的,如果在类的方法表中发现class_index 索引的C是个接口,那就直接抛出java.lang.IncompatibleClassChangeError异常
- 如果通过了第一步,在类中查找是否有简单名称和描述符都与目标匹配的方法,如果有则返回这个方法的直接引用,查找结束
- 否则,在类的父类中递归查找是否有简单名称和描述符都与目标匹配的方法,如果有则返回这个方法的直接引用,查找结束
- 否则,在类实现的接口列表及父接口中递归查找是否有简单名称和描述符都与目标匹配的方法,如果有则返回这个方法的直接引用,查找结束
- 否则,宣告方法查找失败,抛出java.lang.NoSuchMethodError
注意:这里的解析过程是在运行时
这个时候说“为什么子类不能重写父类静态方法”——调用A.f()
这个问题就十分简单了,因为静态方法对应的invokestatic,它所需要的符号引用在类加载阶段符号引用解析成为直接引用了。也就是说它在运行的时候是这样的invokestatic 0xfffff
,根本就不会有上面提到的解析过程——随便你怎么重写,跟我一点关系都没没有。
真相虽然简单,但过程却是非常丰富的,阅读愉快~
- 从JVM角度看为什么子类不能重写父类静态方法
- 子类为什么不能重写父类的静态方法
- 子类为什么不能重写父类的静态方法
- 子类为什么不能重写父类的静态方法
- 子类为什么不能重写父类的静态方法
- 子类为什么不能重写父类的静态方法
- java静态方法的重写,为什么不能?
- java为什么不能重写静态方法
- 为什么java中子类重写父类的方法时声明抛出异常不能比父类范围大
- 子类不能对父类private方法重写
- 从jvm管理角度看java类的静态属性和静态方法----------------转自http://ruixin.iteye.com/blog/897171
- 子类能否重写父类的静态方法
- 子类可以重写父类的静态方法吗
- 从JVM内存管理的角度谈谈JAVA类的静态方法和静态属性
- 从JVM内存管理的角度谈谈JAVA类的静态方法和静态属性
- Java中为什么静态方法不能被重写?为什么静态方法不能隐藏实例方法?
- 子类继承父类(重写父类的静态方法,子类方法必须也是静态的)
- 为什么父类的静态方法没有被重写?
- list中add覆盖处理
- java实现分页技术详解
- 八皇后问题(递归)
- 奥威软件受邀长三角CIO高峰论坛 共探信息未来
- 关于react项目与node后端共用80接口
- 从JVM角度看为什么子类不能重写父类静态方法
- git分支的运用(种类和作用)
- jquery()函数可以接收四种类型的参数
- 每天一个 Linux 命令(57):ss命令
- Android中在Button控件上显示倒计时
- table标签
- 高速ADC时钟jitter对信噪比和有效位数的影响
- 蓝桥杯打印十字
- Struts2升级版本至2.5.10