首先来看看下面这段代码:
public class Base { private int i=5; public Base(){ System.out.println("I come from "+this.getClass()+" i="+this.i); this.display(); } public int getI(){ return i; } public void display(){ System.out.println(this.i); } } public class Sub extends Base { private int i=22; public Sub(){ i=222; } public void display(){ System.out.println("I come from "+this.getClass()+" i="+this.i); } } public class Test { public static void main(String[] args) { Sub sub=new Sub(); System.out.println(sub.getI()); } }
打印结果为:
下面我们来分析一下:
在main函数中首先执行
Sub sub=new Sub();
这行代码。而这行代码须分解为两步:
step1:执行new关键字,在执行这个关键字的时候,有一个疑问:new出来的对象中是否包含了父类中的所有变量(包括私有变量);
step2:执行new后面的括号即初始化对象。从Java核心技术卷一P131种可知初始化数据的步骤为:
1、所有数据被初始化为默认值;
2、按照类声明中出现的次序,依次执行所有域的初始化语句和初始化块;
3、如果构造器第一行调用了第二个构造器,则执行第二个构造器主体;
4、执行这个构造器的主体。
我相信这绝对是一个非常简化版的执行过程了。因为作者这里根本没有提到何时,调用父类的构造器。
那我们现在就来细细分析这个初始化的过程(其实在这篇文章中也分析过了,只是也不是很完整)。
为例更清晰的看看初始化的过程我们使用javap工具看看一个简单的类的字节码:
类代码:
public class SimpleClass { { i=6; } private int i=0; public SimpleClass(){ i=5; } }
字节码为:
这下很清楚了。当对子类Sub初始化时,会首先调用其父类的构造函数,在这时子类Sub只有变量的声明,没有进行任何的赋值操作。jvm开始初始化父类Base:由上图可知,变量定义时赋值和初始化块中赋值在构造器中赋值之前进行。所以首先jvm执行Base中的i=5;此时Base中的被初始化为5了。然后执行构造函数中的语句(请分清构造器中的语句和构造器中的字节码)。接下来的执行见这篇文章。
现在问题是,在Sub初始化进行完之后,Sub包含了父类的那些变量?是不是包含了父类中的所有变量。如果不是(在书上看到是继承了父类非私有的变量),那么执行main函数的这条语句:
System.out.println(sub.getI());
为什么会打印出5呢?因为sub对象中的i变量的值现在为222啊。
注:在李刚老师的《Java程序员基本功》书中看到,子类继承父类的非私有方法,只是将方法放进了自类中,该方法和该子类中其他方法的唯一区别就是多了一个super的标签。那么是不是就可以得出Sub子类中也把父类中的私有变量放到了自己的内存中了,只是我们无法访问而已。
记下疑点,以便慢慢解决……也希望大神们多多指点……