凡是用过面向对象语言的人都写过构造函数(废话一句),java程序员更是如此。由于java是纯面向对象的语言,所以可能造成构造函数满天飞的情况....但是,你知道,当执行构造函数的时候,这短短的一句在JVM中都做了什么吗?
看下面一段代码
class A{
public int varA;
public A(){
System.out.println("inside A");
getVar();
}
public void getVar(){
varA = 321;
System.out.println("varA = " + varA);
}
}
public class B extends A{
public int varB = 123;
public B(){
System.out.println("inside B");
getVar();
System.out.println("varA = " + varA);
}
public void getVar(){
System.out.println("varB = " + varB);
}
public static void main(String[] args){
new B();
}
}
奇怪吧,下面解释一下调用new B()时到底发生了什么
奇怪吧,下面解释一下调用new B()时到底发生了什么
- 当然是调用B的构造函数了
- 由于B是从A继承而来,会默认的调用A的无参构造函数。这里注意,如果A没有提供默认构造函数,而是提供了有参的构造函数,编译将会出错
- 初始化A中的实例变量。由于varA没有在声明时赋值,JVM会将它初始化为0。此时,还没有执行A构造函数中的代码。
- 执行System.out.println("inside A"),输出第一行。然后调用getVar()。这里可以花时间解释一下为什么输出的是varB而不是varA。getVar()是覆盖方法。在java中,覆盖方法是动态决定的,也就是根据拥有它的对象类型来执行,而不是根据引用类型来决定。这一点正好和重载方法相反。因此,这里会执行联编后B的getVar(),而此时varB还没有初始化,所以默认为0
- 执行到这里,A类构造函数的内容已经完毕,返回B的构造函数,执行System.out.println("inside B")
- 此时继续执行getVar(),这时也是执行B的方法,由于varB在声明时已经显示初始化,故输出varB = 123
- 执行System.out.println("varA = " + varA)。从前面可以看出,A类的getVar()根本没有执行,所以varA一直没有被赋值,所以它的值还是0
没想到吧,一句短短的new B()可以引起这么多的调用。其实,如果涉及到静态域或变量,情况要比上面复杂的多,有兴趣的朋友可以试验一下
总结一下,关于调用构造函数创建一个类的过程如下
- 调用构造函数
- 初始化该类的静态变量
- 初始化该类的静态块(static block)
- 调用该类构造函数的第一句(super或this)。如果没有写出,默认调用super()
- 初始化变量/执行语句块
- 执行构造函数剩下的内容