美文网首页
Java中父类的构造函数调用在类变量的初始化之前!!

Java中父类的构造函数调用在类变量的初始化之前!!

作者: 一起来看雷阵雨 | 来源:发表于2018-11-04 16:15 被阅读0次

    如题!以前初步了解过class文件的加载步骤,大部分文章里都提到了:
    类变量的初始化在构造方法之前!!!
    这个很好理解,比如下面的代码

    public class A{
        int a = 1;
        public A(){
          System.out.println(a);  //1
          a = 3;
          System.out.println(a);  //3
        }
    }
    

    上面代码很好理解吧,在构造函数里a已经初始化完成,直接输出1,再次赋值时会覆盖上个值。
    那么我们再看下面的代码:

    public class Main {
    
        public static void main(String[] args){
            new B();
        }
    
        static class A{
            int a = 3;
            public A(){
                System.out.println("this is A "+a);
                a = 2;
                display();
            }
    
            public void display(){
                System.out.println("this is A display "+a);
            }
        }
    
        static class B extends A{
            int a = 1;
            public B(){
                super();
                System.out.println("this is B "+a);
                a = 5;
                display();
            }
    
            @Override
            public void display(){
                System.out.println("this is B display "+a);
            }
        }
    }
    

    输出结果大家还能猜到吗?

    输出结果
    起初看到这个结果我也很诧异!!!
    第二行的结果为什么会是0??不应该是1吗?(方法的重写,调用子类的display,访问子类的a)
    然而事实就是如此,唯一能解释通的就是:
    父类构造方法的调用在类变量的初始化之前!!!
    如果接受这个概念的话,上面的结果就能解释通了,父类的构造方法中调用B的display,而此时B中的a还没有初始化,所以输出默认值,0;
    同样的,下面这几行代码就也能接受了,父类的初始化(也就是父类的加载)必须在子类之前完成,不然这个类变量的super怎么访问父类的属性呢?
    static class B extends A{
            int a = super.a;
            public B(){
                System.out.println("this is B "+a);
                a = 5;
                display();
            }
    }
    

    当然这种只靠猜,说服力可能还不太够,下面是调用javap命令后的输出内容,懂JVM指令的可以自行查看一下!(删除了构造方法中的输出和display中输出的字符串)

    G:\WorkSpace\jedisTest\out\production\jedisTest>javap -c com.company.Main$B
    Compiled from "Main.java"
    class com.company.Main$B extends com.company.Main$A {
      int a;
    
      public com.company.Main$B();
        Code:
           0: aload_0
           1: invokespecial #1                  // Method com/company/Main$A."<init>":()V
           4: aload_0
           5: iconst_1
           6: putfield      #2                  // Field a:I
           9: aload_0
          10: iconst_5
          11: putfield      #2                  // Field a:I
          14: aload_0
          15: invokevirtual #3                  // Method display:()V
          18: return
    
      public void display();
        Code:
           0: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
           3: aload_0
           4: getfield      #2                  // Field a:I
           7: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
          10: return
    }
    

    恕编者目前水平有限,对JVM还不太熟悉,有几个命令还不太熟悉,所以此处不做解释,一段时间后会再来续写,对每一行指令做出解释!但可以告诉大家执行的顺序:
    父类类变量初始化->父类构造函数->子类类变量->子类构造函数。

    相关文章

      网友评论

          本文标题:Java中父类的构造函数调用在类变量的初始化之前!!

          本文链接:https://www.haomeiwen.com/subject/izorxqtx.html