一篇文章教你理解重载与重写

本文根据具体的案例,分析了重载与重写的区别及实现机制,对于理解多态,重载,重写都具有相当大的帮助
摘要由CSDN通过智能技术生成

笔者从事java开发,所以下文中的内容是基于java语言,但是语言具有共同性,其它的语言也可以借鉴

先进行一个测试

public class Test {
        static class A{
                public String format(A b){
                        return "A And A";
                }
                public String format(D b){
                        return "A And D";
                }
        }

        static class B extends A{
                public String format(A b){
                        return "B And A";
                }
                public String format(B b){
                        return "B And D";
                }

        }

        static class C extends  B{  }
        static class D extends  C{ }

    public static void main(String[] args) {
            A a = new B();
            B b = new B();
            B c = new C();
            System.out.println(a.format( b ));
            System.out.println(a.format( c ));
    }
}

答案是

B And A
B And A

如果答成 A and A, A And A 的话,说明你对重写理解有问题,如果答成B And A,B And B的话,说明对重载理解有问题。如果答成其它的答案,那么说明你必须要看此文了。
如果你的结果与正确答案一致,那么恭喜你,你已经理解了重载与重写。如需了解实现机理,可以直接跳到实现机制一节。

理解重载与重写

重载与重写,可以理解成静态绑定与动态绑定 (认为在java中只有statcic,final,private的方法才是静态绑定,而其它方法都是通过通过 invokevirtual实现的读者,此者可以理解成根据方法签名绑定与根据实例绑定(原谅我自己造的这两个词))
下面作两者对一对比

项目重载重写
英文解释overLoadoverride(java中常用)
调用方法确定时机编译期运行期
方法之间的关系在同一个类中在父子类中
方法签名必须不一致必须一致

重载与重写的实现机制

重载

重载,方法签名(函数名及参数类型及个数)必须不一样,不同的重载方法属于同一个类,调用方与被调用方的匹配依据就是方法签名。
比如类A 编译后,其方法列表有 public String format(A), public String format(D), (当然真实的编译后的结果不是这样的,为了简单,此处仅以函数名表示)。 在main函数中,通过类A进行调,参数类型分别为B类及C 类,在编译的时候,会去类A中查找相匹配的方法,因为类A中没有 public String format(B) 及public String format(C),因此通过向上转型为A类,通过调用public String format(A) 实现,即调用的时候,调用的方法签名是public String format(A),验证见下。
将Test类进行反编译,执行javap -c Test.class. 可得到该类的反编译后的结果,看第29行与第40行,可以看到调用的时候传入的类型参数为A

public class com.service.Test {
  public com.service.Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class com/service/Test$B
       3: dup
       4: invokespecial #3                  // Method com/service/Test$B."<init>":()V
       7: astore_1
       8: new           #2                  // class com/service/Test$B
      11: dup
      12: invokespecial #3                  // Method com/service/Test$B."<init>":()V
      15: astore_2
      16: new           #4                  // class com/service/Test$C
      19: dup
      20: invokespecial #5                  // Method com/service/Test$C."<init>":()V
      23: astore_3
      24: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
      27: aload_1
      28: aload_2
      29: invokevirtual #7                  // Method com/service/Test$A.format:(Lcom/service/Test$A;)Ljava/lang/String;
      32: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      35: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
      38: aload_1
      39: aload_3
      40: invokevirtual #7                  // Method com/service/Test$A.format:(Lcom/service/Test$A;)Ljava/lang/String;
      43: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      46: return
}

重写

重写实现的机制是方法表,即每一、个类都有对应的方法表,方法表指向具体的方法实现。
对于上述的类A与类B的方法表可参考如下。(为简单可见,忽略了Object类的toString等方法)
左边是方法表,右边是方法的具体实现, 上面是类A,下面是类B。
这里写图片描述
因此,我们可以看出,一个类如果重写了父类的方法,则该类的方法区会有具体的实现,并且方法表的索引会指向该实现。如果没有重写父类的方法,则方法表中的索引会指向父类的实现方法。因此,当某对象在调用的时候,只需要查看自己方法表中的数据,即可索引到正确的方法上去。

对测试case的解释

回到最初的测试案例,对于在main函数中声明的对象a来讲,是B类对象,因此其执行的依法是从B方法表中寻找相应的方法。
在调用a.format(b) 或 a.format(c) 时,由于在编译器是通过A类对象调用的,在A类中,没有找到参数类型为B类或C类的format方法,因此通过向上转型,找到可以执行的方法是 format(A a),因此将调用代码编译为format(A)(上面反编译中已说明)
因此最终会在B类的方法表中寻找 public String format(A) 的方法,因此输出了

B And A
B and A
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值