A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 布德鸟 于 2016-5-8 18:11 编辑

在看java基础视频时被告知:构造代码块在构造方法前执行(下文称为“定理A”)

但是在学到继承时,我仿佛察觉到了点问题,看以下代码:
  • class Test_Extend {
  •         public static void main(String[] args) {
  •                 Son s = new Son();
  •         }
  • }
  • class Father
  •         {System.out.println("Father的构造代码块");}
  •         public Father(){
  •                 System.out.println("Father的构造方法");
  •         }
  • }
  • class Son extends Father{
  •         {System.out.println("Son的构造代码块");}
  •         public Son(){
  •                 System.out.println("Son的构造方法");
  •         }
  • }
运行输出:
Father的构造代码块
Father的构造方法
Son的构造代码块
Son的构造方法

根据输出结果好似得出跟“定理A”一样的结论:构造代码块在构造方法前执行,且先执行父类后执行子类。
但是,实际上这样理解不准确,我们先看子类的构造方法:
public Son(){
                super();                 //这段代码是必须的,即使我们不写,java编译时会自动加上的
                System.out.println("Son的构造方法");
        }
super(); 是调用父类的构造方法,这里证明了父类构造方法由子类构造方法调用,执行顺序:子类构造方法 > 父类构造方法,但因为super();是子类构造中第一个执行的语句,所以才有看似先执行父类构造方法的效果。

接着我们看一下输出结果:“Father的构造方法”先于“Son的构造代码块”,说明了执行顺序:父类构造方法 > 子类构造代码块,结合上面的执行顺序:子类构造方法 > 父类构造方法,得出执行顺序:子类构造方法 > 子类构造代码块

那么问题来了既然执行顺序:子类构造方法 > 子类构造代码块,那如何保证输出结果“Son的构造代码块”先于“Son的构造方法”?

其实也不难,以下是我的猜想:
与子类构造方法调用父类构造方法的效果一样,在构造方法内部的最顶端添加调用执行构造代码块的语句,如下:
public Son(){
                super();                 //这段代码是必须的,即使我们不写,java编译时会自动加上的
                "执行本类构造代码块的语句";                //这段代码也是系统自动添加的,这段代码需要在排在super();下,因为要保证父类的先执行。
                System.out.println("Son的构造方法");
        }

这样子就能保证输出结果“Son的构造代码块”先于“Son的构造方法”了。
然后由个体试推整体的规律:在每个类的构造方法中的最顶端,不仅隐含着super();这个语句,还隐含着"执行本类构造代码块的语句";(下文称“定理B”)
然后我们把隐含代码显示如下:
  • class Test_Extend {
  •         public static void main(String[] args) {
  •                 Son s = new Son();
  •         }
  • }
  • class Father
  •         {System.out.println("Father的构造代码块");}
  •         public Father(){
  •                 super();
  •                 “执行本类构造代码块的语句”;
  •                 System.out.println("Father的构造方法");
  •         }
  • }
  • class Son extends Father{
  •         {System.out.println("Son的构造代码块");}
  •         public Son(){
  •                 super();
  •                 "执行本类构造代码块的语句";
  •                 System.out.println("Son的构造方法");
  •         }
  • }

然后一切都说的通了,我们来梳理一下执行过程:
①main方法里new一个Son对象
②Son类的构造方法Son()开始执行,super();调用父类的构造方法Father()
③Father()中的super();调用Object类的构造方法(Object类的构造方法内的执行过程略过)
④Father()中的"执行本类构造代码块的语句";调用Father类的构造代码块{System.out.println("Father的构造代码块");},输出“Father的构造代码块”
⑤Father()中的System.out.println("Father的构造方法");执行,输出"Father的构造方法"
⑤Son()中的"执行本类构造代码块的语句";调用Son类的构造代码块{System.out.println("Son的构造代码块");},输出“Son的构造代码块”
⑥Son()中的System.out.println("Son的构造方法");执行,输出"Son的构造方法"

推导的输出结果与实际结果不谋而合,本例的执行顺序应该是:子类的构造方法 > 父类的构造方法 > 父类的构造代码块 > 子类的构造代码块

所以我推荐大家用“定理B”(我目前没找到权威的资料证实,不过比较符合实际):在每个类(除了Object)的构造方法中的最顶端,不仅隐含着super();这个语句,还隐含着"执行本类构造代码块的语句";
构造代码块不是单纯的先于构造方法执行,而是在构造方法中会首先执行的一批调用语句,(调用语句有super();、"执行本类构造代码块的语句";等,且super();先于"执行本类构造代码块的语句";)。

最后,祝各位的母亲,节日快乐!~

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马