父类构造函数调用了子类中重写的方法,以及变量初始化过程,这个过程到底发生了什么???

        在我的下面这篇博客(链接如下)中有几个例题,其中关于父类构造函数中调用了子类中重写方法的过程中,到底发生了什么,我原文博客里也有解释,由于篇幅挺大,为了方便阅读和体现专题性,所以将其从原文中拿出来,单独写成博客 。

        本人博客原文:https://blog.csdn.net/weixin_37766087/article/details/94489132

        由一个示例引发的血案,查了很多博客,都没有给出很清晰的解释,下面记录自己的分析过程。

       下面是示例的源代码:

public class App {
 
public static void main(String[] args)throws Exception{
 
      Sub b=new Sub();   
 
      b.callName();    //这一步是多态的 使用,很好理解
}
 
}
 
class Base{
      private String baseName="base";
 
      public Base(){
 
            System.out.println("hellobase");  
 
            callName();
 
      }
 
      public void callName(){
            System.out.println(baseName);
      } 
}
 
class Sub extends Base{
      private String baseName="sub";
 
      public void callName(){
            System.out.println("hello");
 
            System.out.println(baseName);
      }
} 
}

        结果输出是null  为什么???

        Base b = new Sub();先初始化父类,然后调用子类的callName方法时子类的属性还没有初始化执行代码,所以打印的是null.

        为了解决上面的疑问,可算是费劲苦心,好在结果不错,有过程,有结论,最起码说服了自己。

     (经过个人的调试,终于发现了构造类时 变量的初始化顺序的秘密)

       为了方便,本人重新写了一份代码,下面记录我的代码执行顺序:
 

1 public class ForTest {
		public static void main(String[] args){
2			A b=new B();   //加上断点  
		}
}
 
 class A{                    
3	int a=3;             //加上断点
	public A(){
        //默认super()    //这个也可以显示写出来,并加上断点,会发现,第一步走super()
4		System.out.println(a);   //加上断点
5		method();
	}
7	public void method(){
6		System.out.println(a);
	}
}
class B extends A{
8	int a=2;   //加上断点
	public B(){
9		System.out.println("hello"); 
10		System.out.println(a);
	}
	public void method(){
11		System.out.println(a);   //加上断点
	}
}

从上一步到下一步都是点击step over:

注:下面每一步的行对应的是上方代码区里面的标注行,有标注,共11个标注行。

 

step 1:进入调试  代码在 行2 处

step 2:  代码在行3 处。

        想一下为什么一下就到了父类A中的变量赋值处?实际上在new子类的时候,调用了父类A的构造函数,你所看到的是一下子就到了父类A中的变量初始化这儿,也就是说变量在new一个类对象的时候,先执行的变量初始化的,再执行构造函数,如果仅仅是这样记忆,又会产生疑惑,你肯定会想,那这里在从父类构造函数中调用子类的callName的时候,肯定能打印出a=2 这个值啊,但是实际上打印的是0,也就是说此时的a还是默认初始值0,那难道上面说的new对象的时候 先执行变量初始化是错的???实际上,一般情况下,不用考虑父类构造函数调用子类重写的方法的时候,这样记忆确实是对的,但是原理是什么,原理这样是错的,原理上,并不是初始化先执行,下面给出我的分析:

        根本原因在于,这里忘了分析最最关键的一步,实际上,new类对象的时候的时候,最先执行的还是构造函数,这里执行的一定是构造函数的第一步super()函数,它去首先构造了父类的对象,所有类都是Object的子类,所以会一直向上构造父类,直到Object,当父类构造完毕,super执行完毕,再返回本类中,从代码块,变量的初始化开始执行,这里要注意,此时,本类构造函数super()函数执行完毕,并没有继续执行构造函数中super()函数下面的语句,而是去执行了变量的初始化,然后当构造代码块和变量初始化完毕,再继续执行构造函数super()以下的语句,所以导致一种假象,我们看起来就是先执行了构造代码块和变量的初始化,最后再执行构造函数,实际上是第一步执行了构造函数,但是只执行了super(),它去构造父类对象去了。

       为了验证我上面的分析,在new一个对象的时候,我们可以将super()函数显示写在构造函数中,再打上断点,你会发现,一定是先执行super()函数(如果该类父类不是Object,那么就会走到父类的构造函数,如果该类的父类是Object,也会走到父类Object,只不过Object是一种特殊的类,与jvm底层相关),然后等父类构造完毕之后,才会执行变量的初始化。

step3: 到达4 ,此时打印a,值为3

step 4 :到达5,

step 5: 到达11 ,这里父类构造函数中调用的重写方法,是子类的,为什么?看原文博客(博客链接在第一行)示例3后面的解析。注意,此时a的输出是0,为什么??经过step2的分析可知:此时父类对象还没有构造完成,也就是说B中构造函数中的super()还没有执行完,此时,并不会进行B中变量的初始化,所以a是默认的初始值0.

step 6: 到达8 ,此时,才会对变量a进行赋值,B的父类已经构造完毕。

step7: 到达9    ,step7和step8是执行构造函数中super剩下的步骤。

step8: 到达10

step 9:到达2 ,至此,新对象B的构造过程才算是结束!

 

图解:为了将上述执行顺序看得更加清楚,将每一步标注成红色的序号,如下:

红色的标号是断点执行顺序,凡事标注红色序号的代码处都要打上断点才能显示顺序,这里有些地方没有打断点。

总结:

new构造函数的时候

1 先执行构造函数中的super()函数,它去构造父类对象;

2 构造完毕之后,返回本类执行变量和构造代码块的内容;

3 继续执行构造函数中super()以下的内容。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值