先看一道java面试题:
1publicclassBaset {2PRivateString baseName = "base";3//构造方法4publicBaset() {5callName();6}7//成员方法8publicvoidcallName() {9//TODO Auto-generated method stub10System.out.println("basename:" +baseName);11}12//静态内部类13staticclassSubextendsBaset {//static必须写在开头14//静态字段15privateString baseName = "sub";16publicSub() {17callName();18}19//重写父类的方法20publicvoidcallName() {21System.out.println("subname:" +baseName);22}23}2425publicstaticvoidmain(String[] args) {26Baset base =newSub();27}28}
求这段程序的输出。
解答此题关键在于理解和掌握类的加载过程以及子类继承父类后,重写方法的调用问题:
一、从程序的执行顺序去解答:
1.编译;当这个类被编译通知后,会在相应的目录下生成两个.class 文件。一个是Base.class,另外一个就是Base$Sub.class。这个时候类加载器将这两个.class 文件加载到内存
2、Base base= new Sub():
声明父类变量base对子类的引用,JAVA类加载器将Base,Sub类加载到JVM(Java虚拟机);
3、JVM为Base,Sub 的的成员开辟内存空间
此时,Base 和Sub类中的值为null;
4、new Sub();
这个时候会调用Sub类的隐式构造方法,
Sub的构造方法本质为:
public Sub(){
super();// 调用父类的构造方法。必须在构造方法中的第一行,为什么呢?这是因为在一些程序的升级中,要兼容旧版本的一些功能,父类即原先的一些初始化信息也要保证 //被执行到,然后执行当前
baseName = "sub";//子类字段初始化
}
new Sub()执行到super()这行代码也就是跑到父类中去执行了,我们跳转到父类中的无参构造方法中执行,最后执行Sub()中的baseName = "sub"
5、public Base() ;
父类无参构造方法的本质为:
public Base(){
baseName= "base";//父类字段初始化
callName();
}
即将父类的baseName赋值为“base”,赋值后调用callName();
6、callName()方法在子类中被重写,因此调用子类的callName(),子类的callName方法执行,打印输出的是子类的baseName 字段的值,而这个时候子类的构造函数中字段的赋值还未执行。
7、父类的构造函数执行完毕,这个时候又回到子类当中,从super()的下一行继续执行,这个时候才为子类字段baseName 分配好存储空间,随后为其赋值:
可见,在baseName = "sub"执行前,子类的callName()已经执行,所以子类的baseName为默认值状态null;
二、另写两个程序,进行更具体的分析
1、第一个程序:
1publicclassInitialOrderTest {2//变量3publicString field = "变量";4//静态变量5publicstaticString staticField="静态变量";6//父类静态方法7publicstaticvoidOrder(){8System.out.print("父类静态方法");9System.out.println("staticField:"+staticField);10}1112//静态初始代码块13static{14System.out.println("静态初始化块");15System.out.println("staticField:"+staticField);16}17//初始化代码块18{19System.out.println("初始化代码块");20System.out.println("field:"+field);21}22//构造函数23publicInitialOrderTest(){24System.out.println("构造器");2526}2728publicstaticvoidmain(String[] args) {29System.out.println("-----[[-------");30System.out.println(InitialOrderTest.staticField);31InitialOrderTest.Order();32System.out.println("------]]------");33InitialOrderTest i =newInitialOrderTest();34System.out.println("-----[[-------");35System.out.println(InitialOrderTest.staticField);36InitialOrderTest.Order();37System.out.println("------]]------");3839}40}
执行结果为:
第一个程序总结:
1)、java中的块分为静态块(static{})和非静态块({}),这两种的执行是有区别的:
非静态块的执行时间是:在执行构造函数之前。 静态块的执行时间是:class文件加载时执行。
static类型的属性也是在类加载时执行的。
2)、可见Java类的实例变量初始化的过程:
static类型的成员属性执行,静态块(static{})按顺序执行,然后非静态成员变量初始化,非静态代码块({})执行,最后执行构造方法。
static类型与static块按先后顺序执行。
---------------------------------------------------------------------------------------------------------------------------------------------------
2、第二个程序:
1publicclassBaseTest {2//父类变量3privateString baseName = "base";4//父类静态变量5privatestaticString staticField = "父类静态变量";6//父类静态方法7publicstaticvoidOrder() {8System.out.println("父类静态方法-");9System.out.println("staticField:" +staticField);10}11//父类静态初始代码块12static{13System.out.println("父类静态初始化代码块-");14System.out.println("staticField:" +staticField);15}16//初始化代码块17{18System.out.println("父类非静态初始化代码块-");19System.out.println("baseName:" +baseName);20}21//构造函数22publicBaseTest() {23System.out.println("父类构造方法");24callName();25}26//成员方法27publicvoidcallName() {28System.out.println("父类callName方法-");29System.out.println("baseName:" +baseName);30}3132//静态内部类33staticclassSubextendsBaseTest {34//子类变量35privateString baseName = "sub";36//子类 静态变量37privatestaticString staticField = "子类静态变量";3839//子类静态方法40publicstaticvoidOrder() {41System.out.println("子类静态方法-");42System.out.println("staticField:" +staticField);43}4445//子类静态初始化代码块46static{47System.out.println("子类静态初始化代码块-");48System.out.println("staticField:" +staticField);49}50//子类非静态初始化代码块51{52System.out.println("子类非静态初始化代码块-");53System.out.println("baseName:" +baseName);54}5556publicSub() {57System.out.println("子类构造方法");58callName();59}6061publicvoidcallName() {62System.out.println("子类重写父类callName方法-");63System.out.println("baseName:" +baseName);64}65}6667publicstaticvoidmain(String[] args) {6869BaseTest b =newSub();7071System.out.println("-----[[-------");72Sub.Order();73System.out.println(Sub.staticField);74System.out.println(BaseTest.staticField);75BaseTest.Order();76System.out.println("------]]------");7778}79}
执行结果:
第二个程序总结:
1)、可见Java初始化的顺序:
11父类静态变量初始化22 父类静态代码块-static{ }33子类静态变量初始化44 子类静态代码块-static{ }55父类非静态变量初始化66 父类非静态代码块-{}77父类构造方法88子类非静态变量初始化99 子类非静态代码块-{}1010 子类构造方法