work hard work smart

专注于Java后端开发。 不断总结,举一反三。
随笔 - 1158, 文章 - 0, 评论 - 153, 阅读 - 186万
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

方法重载与invokevirtual字节码指令的关系

Posted on   work hard work smart  阅读(908)  评论(1编辑  收藏  举报

1、方法重载

创建MyTest5类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class MyTest5 {
 
    public void test(Grandpa grandpa){
        System.out.println("grandpa");
    }
 
    public void test(Father father){
        System.out.println("father");
    }
    public void test(Son son){
        System.out.println("son");
    }
 
 
    public static void main(String[] args) {
        Grandpa g1 = new Father();
        Grandpa g2 = new Son();
 
        MyTest5 myTest5 = new MyTest5();
 
        myTest5.test(g1);
        myTest5.test(g2);
    }
}
 
 
 
class Grandpa{
 
}
 
class Father extends Grandpa{
 
}
 
class Son extends Father{
 
}

  输出结果如下:

 

2、方法的静态分派
Grandpa g1 = new Father();
以上代码,g1的静态类型是Grandpa,而g1的实际类型(真正的指向的类型)是Father
我们可以得出这样一个结论:变量的静态类型是不会发生变化的,而实际类型则可以发生变化的(多态的一种体现),实际类型是在运行期方可确定。

方法重载,是一种静态的行为,编译期就可以完全确定。

所以MyTest5最终的输出结果两个都是grandpa

使用jclasslib,调用两个test方法,使用的都是invokevirtual指令

 

3、方法的动态分派机制

 创建MyTest6.java类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.example.jvm.bytecode;
 
public class MyTest6 {
 
    public static void main(String[] args) {
        Fruit apple = new Apple();
        Fruit orange = new Orange();
 
        apple.test();
        orange.test();
 
        apple = new Orange();
        apple.test();
    }
 
}
class Fruit{
    public  void test(){
        System.out.println("Fruit");
    }
}
 
class Apple extends Fruit{
 
    @Override
    public void test() {
        System.out.println("Apple");
    }
}
 
class  Orange extends Fruit{
    @Override
    public void test() {
        System.out.println("orange");
    }
}

  输出结果如下:

Apple
orange
orange

 

使用jclasslib查看字节码

可以看到apple.test() 对应的指令为 invokevirtual #6 <com/example/jvm/bytecode/Fruit.test>  在编译的时候使用的类型为Fruit,并不知道真正的类型为Apple

 

方法的动态分派
方法的动态分派涉及到一个重要概念: 方法接收者。
invokevirtual字节码指令的多态查找流程(执行期)
1、找到操作数栈顶的第一个元素,它所指向对象的实际类型
2、在实际对象中找对应的方法(test()方法),检查访问类型是否可以访问,找到了就调用。如果没找到,继续往上找。

 

比较方法重载(overload)与方法重写(overwrite),我们可以得到这样的结论
方法重载是静态的,是编译器行为; 方法重写是动态的,是运行期行为。

编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
点击右上角即可分享
微信分享提示