子类和父类中方法调用问题总结

昨天晚上,自己在牛客网上刷了一道关于父类子类方法引用的问题,当时感觉很蒙蔽,已经上床了,就带着问题直接睡了,今早起来,把问题复现,自己又深入的了解了一下,打算记录一下这次收获的东西,希望能够帮助到大家。

问题原文
public class Test {
    public static void main(String[] args) {
        System.out.println(new B.getValue());
    }

    static class A {
        protected int value;

        public A (int value) {
            setValue(value);
       }

        public void setValue(int value) {
            this.value = value;
        }

        public int getValue() {
            try {
                value++;
                return value;
            } finally {
                this.setValue(value);
                System.out.println(value);
            }
        }
    }

    static class B extends A {
        public B () {
            super(5);
            int value = getValue() - 3;
            setValue(value);
        }

        public void setValue(int value) {
            super.setValue(2 * value);
        }
    }

问这段代码的执行结果是啥,当时没有整明白,今天早上一边debug,一边总结自己遗漏的知识点,下面就这个问题进行一些自己的分享和理解。

在父类和子类方法引用问题的这一块,自己之前一致都在含含糊糊,不知道具体是怎样执行的,今天细细捋一遍,主要有以下几点:
1、当子类继承父类之后,子类可以通过使用super关键字,去调用父类的方法,如果不用这个关键字,则默认调用的是自己的实例方法,而对于父类来说,如果子类实现了自己定义的方法,则每次调用的时候,是调用的子类的方法,若没有实现,则调用自己的方法。
2、在构造器调用这部分,子类可以使用super关键字调用父类的构造器,要确保传入参数正确。
3、关于子类父类的各个模块的加载顺序问题:
父类静态代码块 ---- 子类静态代码块 ---- 父类非静态代码块 ---- 父类构造函数 ---- 子类非静态代码块 ---- 子类构造函数。

下面是自己的源代码分析,自己可以再使用DEBUG进行调试测试,也是根据自己的理解,有哪里不对的地方,希望能够给sulay指出来,大家一同学习。

public class VariableReferenceTest {

    public static void main(String[] args) {
        // 当我们在代码中执行new语句时,会默认去查找对应类的构造器
        System.out.println(new SubClass().getValue());
    }
    static class SuperClass {
        protected int value;

        // 如果这个类被其子类所实现,子类采用super关键字,就能够找到这个构造器
        public SuperClass(int value) {
            System.out.println(value);
            // 这里默认执行的是子类的方法,不回去执行父类的方法,
            // 父类方法在子类进行super.methodName调用时,才能够被调用得到
            setValueV1(value);
            // 调用的是子类的方法
            setValue(value);

            System.out.println(this.value);

        }

        public void setValueV1(int value) {
            this.value = value;
        }

        public void setValue(int value) {
            this.value = value;
        }

        public int getValue() {
            System.out.println("SuperClass--------------getValue()");
            try {
                value++;
                System.out.println("value++-----------value => " + value);
                // 这里会先去执行finally语句块,之后才会回来执行return语句,但是,在finally语句块中对于value值得修改,在这里没有同步???????
                return value;
            } finally {
                this.setValue(value);
                System.out.println("this.setValue----------value => " + value);
            }
        }
    }

    static class SubClass extends SuperClass {

        // new之后,找到了这个类的构造器,开始执行构造器中的代码
        public SubClass() {
            // super关键字去找到继承父类的构造器
            super(5);
            // 这里调用getValue()方法进入子类的方法中,在子类的方法中,调用了父类的同名方法,
            // 但是在父类中,finally代码块中对于value值的更改,不会同步到try代码块中
            int value = getValue() - 3;
            System.out.println(value);
            setValue(value);
        }

        public void setValue(int value) {
            super.setValue(2 * value);
        }

        public int getValue() {
            // 死循环,一致递归调用getValue()方法
//            int result = this.getValue();
            System.out.println("SubClass-------------getValue()");
            int result = super.getValue();
            System.out.println(result);
            return result;
        }
    }
}
虚拟机字节码文件

碰巧自己最近在学习JVM,就将上面的代码使用javap工具进行了类文件的字节码文件输出,具体执行语句是:

>javac VariableReferenceTest.java
>javap -verbose VariableReferenceTest.class

执行结束之后,你可以获得上面源代码的字节码内容,这里简要进行分析一下这个文件中的内容:

# 类文件的一些属性值,包括全路径,最近更改时间,是被哪个Java源文件编译而来的
Classfile /E:/IDEA_Storage/code/src/code/javase/classes/VariableReference.class
  Last modified 2020-12-11; size 660 bytes
  MD5 checksum 2d62e3581d7fe6857922e25f34f16212
  Compiled from "VariableReference.java"
# 这里说明了该类的一些属性,主版本号、次版本号和标识位
public class code.javase.classes.VariableReference
  minor version: 0
  major version: 55
  flags: ACC_PUBLIC, ACC_SUPER
# 这是该类的常量池信息
Constant pool:
   #1 = Methodref          #8.#22         // java/lang/Object."<init>":()V
   #2 = Fieldref           #23.#24        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Class              #25            // code/javase/classes/VariableReference$SubClass
   #4 = Methodref          #3.#22         // code/javase/classes/VariableReference$SubClass."<init>":()V
   #5 = Methodref          #3.#26         // code/javase/classes/VariableReference$SubClass.getValue:()I
   #6 = Methodref          #27.#28        // java/io/PrintStream.println:(I)V
   #7 = Class              #29            // code/javase/classes/VariableReference
   #8 = Class              #30            // java/lang/Object
   #9 = Utf8               SubClass
  #10 = Utf8               InnerClasses
  #11 = Class              #31            // code/javase/classes/VariableReference$SuperClass
  #12 = Utf8               SuperClass
  #13 = Utf8               <init>
  #14 = Utf8               ()V
  #15 = Utf8               Code
  #16 = Utf8               LineNumberTable
  #17 = Utf8               main
  #18 = Utf8               ([Ljava/lang/String;)V
  #19 = Utf8               SourceFile
  #20 = Utf8               VariableReference.java
  #21 = Utf8               NestMembers
  #22 = NameAndType        #13:#14        // "<init>":()V
  #23 = Class              #32            // java/lang/System
  #24 = NameAndType        #33:#34        // out:Ljava/io/PrintStream;
  #25 = Utf8               code/javase/classes/VariableReference$SubClass
  #26 = NameAndType        #35:#36        // getValue:()I
  #27 = Class              #37            // java/io/PrintStream
  #28 = NameAndType        #38:#39        // println:(I)V
  #29 = Utf8               code/javase/classes/VariableReference
  #30 = Utf8               java/lang/Object
  #31 = Utf8               code/javase/classes/VariableReference$SuperClass
  #32 = Utf8               java/lang/System
  #33 = Utf8               out
  #34 = Utf8               Ljava/io/PrintStream;
  #35 = Utf8               getValue
  #36 = Utf8               ()I
  #37 = Utf8               java/io/PrintStream
  #38 = Utf8               println
  #39 = Utf8               (I)V
{
  public code.javase.classes.VariableReference();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: new           #3                  // class code/javase/classes/VariableReference$SubClass
         6: dup
         7: invokespecial #4                  // Method code/javase/classes/VariableReference$SubClass."<init>":()V
        10: invokevirtual #5                  // Method code/javase/classes/VariableReference$SubClass.getValue:()I
        13: invokevirtual #6                  // Method java/io/PrintStream.println:(I)V
        16: return
      LineNumberTable:
        line 5: 0
        line 6: 16
}
SourceFile: "VariableReference.java"
Error: unknown attribute
  NestMembers: length = 0x6
   00 02 00 03 00 0B
InnerClasses:
     static #9= #3 of #7; //SubClass=class code/javase/classes/VariableReference$SubClass of class code/javase/classes/VariableReference
     static #12= #11 of #7; //SuperClass=class code/javase/classes/VariableReference$SuperClass of class code/javase/classes/VariableReference


这其中涉及到了虚拟机的一些操作指令,感兴趣的可以先自行阅读参考。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值