【黑马程序员济南中心】继承中的构造代码块执行顺序
相信有很多的同学都遇到了这样一道面试题目,题目是请问程序的执行结果,代码如下:
class Test2_Extends {
public static void main(String[] args) {
Zi z = new Zi();
}
}
class Fu {
static {
System.out.println("静态代码块Fu");
}
{
System.out.println("构造代码块Fu");
}
public Fu() {
System.out.println("构造方法Fu");
}
}
class Zi extends Fu {
static {
System.out.println("静态代码块Zi");
}
{
System.out.println("构造代码块Zi");
}
public Zi() {
System.out.println("构造方法Zi");
}
}
下面我们循序渐进的来研究一下这个题目,
首先,我们先来讲讲什么是代码块:
代码块就是用{}括起来的代码,根据代码块的功能、执行顺序和书写位置,我们把代码块分为 静态代码块,构造代码块,局部代码块。
静态代码块:书写在成员位置, 大括号前面有static关键字,仅随着类的加载执行一次,所以可以在里面一些项目中的初始化的内容。
构造代码块:书写在成员位置,每次创建对象的时候构造代码块都会执行一次,而且是在构造方法的内容执行之前执行,所以可以写一些该类里面所有构造方法共性的内容。
局部代码块:书写在局部位置(即方法里面),可以尽早的让变量在内存中消失,略微提升一些效率
接着 , 我们按照上面对代码块的理解去分析一下这道的面试题的答案:
首先执行 Zi z = new Zi();创建Zi类对象的时候,必须先加载Zi类的父类Fu,接着加载Zi类,那么Fu类和Zi类中的静态代码块就会随着类的加载而依次执行了,然后程序即将执行Zi类的构造方法,但是在构造方法执行之前必须先执行Zi类的构造代码块,接着执行Zi类的构造方法, 由于Zi类的构造方法第一行语句默认都有super(),所以接着将要去执行父类Fu的构造方法,但是在执行Fu的构造方法之前,先执行Fu的构造代码块,然后才能执行Fu的构造方法,执行Fu的这些内容后,才接着执行Zi类构造方法的后续内容。那照这样看来,执行结果应该是如下这样的
静态代码块Fu
静态代码块Zi
构造代码块Zi
构造代码块Fu
构造方法Fu
构造方法Zi
但是 , 事实并非如此,我们实际运行了一下 却发现执行结果是如下这样的:
静态代码块Fu
静态代码块Zi
构造代码块Fu
构造方法Fu
构造代码块Zi
构造方法Zi
这究竟是为什么呢?为什么和我们分析结果不一样呢?这其实与构造代码块被编译到的位置有关。
接下来,我就利用反编译(反编译就是把class文件转成java文件)技术来解释一下这道面试题:首先介绍一个反编译工具"jd-gui.exe"(详见附件),打开反编译工具,将次此程序的class文件拖进反编译工具,你会发现反编译出来的代码和原来的代码有一些差异,代码如下:
class Test2_Extends
{
public static void main(String[] paramArrayOfString)
{
Zi localZi = new Zi();
}
}
class Fu
{
public Fu()
{
System.out.println("构造代码块Fu");
System.out.println("构造方法Fu");
}
static
{
System.out.println("静态代码块Fu");
}
}
class Zi extends Fu
{
public Zi()
{
System.out.println("构造代码块Zi");
System.out.println("构造方法Zi");
}
static
{
System.out.println("静态代码块Zi");
}
}
大家仔细查看反编译出来的代码 和原来的代码有什么区别呢?
很明显,就是反编译出来的代码,构造代码块被写到了构造方法的最上面一行(别忘了这一行的上面还有super()呢),根据这个代码我们不难理解题目运行的结果
静态代码块Fu
静态代码块Zi
构造代码块Fu
构造方法Fu
构造代码块Zi
构造方法Zi
其实讲到现在,我相信大家已经理解了上面的面试题,这道题最关键的理解点就是 构造代码块在编译期间其实是编译到了构造方法里面super()语句下面的位置。