重写和重载

https://blog.51cto.com/14687333/2537239

相关概念

  • 静态类型和实际类型

    Animal jack = new Dog()
    

    jack的静态类型为Animal, 实际类型为Dog

  • 虚方法和非虚方法

    非虚方法:方法在编译器就确定了具体的调用版本,则这个版本在运行时是不可变的,这样的方法称为非虚方法。

    如静态方法、私有方法、final方法、实例构造器、父类方法都是非虚方法。

    其他方法为虚方法

  • 方法调用指令

    invokestatic 调用静态方法,解析阶段(类加载的链接)确定唯一方法版本

    Invokespecial 调用方法,私有及父类方法, 也是解析阶段确定唯一方法版本

    invokevirtual 调用所有虚方法,但是final方法也用这个调用

    invokeinterface 调用接口方法

    前两条指令在类加载的解析阶段就能把符号引用解析为直接引用。

  • 方法重写和方法重载

    对重载方法的调用主要看静态类型,静态类型是什么类型,就调用什么类型的参数方法。

    举例:

    public class DemoApplication {
    
    	public static void main(String[] args) throws Exception {
    		Est est = new Est();
    		Animal animal = new Dog();
    		est.fun((Dog)animal);   // 输出dog
        est.fun(aniaml) 				// 输出animal
        est.fun(new Dog())      // 输出dog
    	}
    }
    
    class Est {
    	public void fun(Animal animal) {
    		System.out.println("animal");
    	}
    
    	public void fun(Dog dog) {
    		System.out.println("dog");
    	}
    }
    

    对重写方法的调用主要看实际类型。实际类型如果实现了该方法则直接调用该方法,如果没有实现,则在继承关系中从低到高搜索有无实现。

方法重写

abstract class Animal {
	abstract void say();
}

class Dog extends Animal {

	@Override
	void say() {
		System.out.println("汪汪");
	}
}

class Cat extends Animal {

	@Override
	void say() {
		System.out.println("喵喵");
	}

	public static void main(String[] args) {
		Animal dog = new Dog();
		Animal cat = new Cat();
		dog.say();
		cat.say();
	}
}

字节码文件:

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: new           #5                  // class com/example/demo/Dog
         3: dup
         4: invokespecial #6                  // Method com/example/demo/Dog."<init>":()V
         7: astore_1
         8: new           #7                  // class com/example/demo/Cat
        11: dup
        12: invokespecial #8                  // Method "<init>":()V
        15: astore_2
        16: aload_1
        17: invokevirtual #9                  // Method com/example/demo/Animal.say:()V
        20: aload_2
        21: invokevirtual #9                  // Method com/example/demo/Animal.say:()V
        24: return

可以发现调用的都是 invokevirtual #9 // Method com/example/demo/Animal.say:()V ,但是实际上却调用的是不同的方法。

因为,在编译阶段,编译器只知道对象的静态类型,而不知道实际类型,所以在class文件中只能确定要调用父类的方法。但是在执行时却会判断对象的实际类型。如果实际类型实现这个方法,则直接调用,如果没有实现,则按照继承关系从下往上一次检索,只要检索到就调用,如果始终没有检索到,则抛异常

在这里插入图片描述

在以上查找的基础上,由于动态分派非常频繁,JVM 运行时会频繁的、反复的去搜索元数据,所以 JVM 使用了一种优化手段,在方法区建立一个虚方法表,使用虚方法表索引来代替元数据查找(invokevirtual的多态查找流程)以提高性能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值