技术咨询:请求详解method hiding(方法隐藏)的底层运行机制
Hey there! Let me break down method hiding for you clearly—no confusing jargon, just straight talk about what's happening under the hood.
先划重点:方法隐藏≠方法重写(Override)
很多人会把这俩搞混,所以先明确核心差异:
- 方法重写是针对实例方法的动态绑定行为(运行时才决定调用哪个方法);
- 方法隐藏则是针对静态方法(或父类私有方法)的静态绑定行为(编译阶段就锁定调用目标)。
(1) 方法隐藏实际执行了什么操作?
简单来说:当子类定义了一个和父类同名、同参数列表的静态方法时,子类的方法会“隐藏”父类的同名静态方法——但这不是“覆盖”,而是在编译期就固定了方法的调用指向,运行时不会根据对象的实际类型动态切换调用目标。
核心本质是:方法隐藏是编译阶段的静态绑定行为,JVM不会在运行时去检查对象的实际类型,只会看你用来调用方法的变量的「编译时类型」。
(2) 这些操作具体是怎样的?
我用代码例子一步步拆解给你看:
假设我们有父类和子类的静态方法定义:
class Animal { public static void eat() { System.out.println("Animal is eating"); } } class Cat extends Animal { public static void eat() { System.out.println("Cat is eating"); } }
步骤1:编译阶段——锁定方法调用目标
当你写这样的代码时:
Animal animalRef = new Cat(); animalRef.eat(); // 调用的是Animal的eat(),不是Cat的!
编译器在编译时,只会关注animalRef的编译时类型(也就是Animal),所以它会直接绑定到Animal类的eat()静态方法,在生成的字节码里写入invokestatic指令,明确指向Animal.eat()。
而如果是这样的代码:
Cat catRef = new Cat(); catRef.eat(); // 调用的是Cat的eat()
编译器看到catRef的编译时类型是Cat,就会绑定到Cat类的eat()方法,字节码里的invokestatic指令直接指向Cat.eat()。
步骤2:运行阶段——直接执行编译期绑定的方法
运行时JVM看到invokestatic指令,会直接执行指令里指定的类的静态方法——完全不会去检查animalRef实际指向的对象是Cat类型。因为静态方法属于类本身,不属于实例,根本不需要依赖对象实例来确定调用目标。
对比方法重写,更能凸显差异
如果是实例方法的重写场景:
class Animal { public void sleep() { System.out.println("Animal is sleeping"); } } class Cat extends Animal { @Override public void sleep() { System.out.println("Cat is sleeping"); } }
这时执行:
Animal animalRef = new Cat(); animalRef.sleep(); // 调用的是Cat的sleep()
编译器会生成invokevirtual指令,运行时JVM会根据animalRef实际指向的Cat对象,动态调用Cat的sleep()方法——这就是和方法隐藏完全不同的动态绑定逻辑。
额外补充:私有方法的隐藏
如果父类有个私有方法,子类定义了同名同参数的方法,这也算方法隐藏,但本质是子类的方法和父类的方法完全无关——因为私有方法不能被继承,子类的方法是全新的,只是名字碰巧一样。比如:
class Animal { private void run() { System.out.println("Animal runs"); } } class Cat extends Animal { public void run() { System.out.println("Cat runs"); } }
这时用Cat实例调用run(),完全和父类的run()无关,因为父类的run()对子类不可见,子类的run()是独立的方法,这也是一种隐藏,逻辑和静态方法隐藏类似:编译期就确定了调用目标。
内容的提问来源于stack exchange,提问作者Tom R

