一道阿里巴巴笔试题中,关于java中的变量,初始化快,构造函数初始化分析

     本人菜鸟一只,断断续续学习java一年有余,之前一只都是转载各位大神的帖子,不敢献丑。前几天找工作遇到了一道笔试试题,让我困惑许久。查了一些资料,加上一些自己的理解,终于算是大体上明白了,也下决心写下自己的第一篇技术类博客(由于本人实在技术有限,如有错误,还请指正,感激不尽),当做是一个学习笔记吧,激励自己不断进步。

      笔试试题是,给出下面代码的输出:

public class Test1{
	public static int k=0;
	public static Test1 t1=new Test1("t1");
	public static Test1 t2=new Test1("t2");
	public static int i=print("i");
	public static int n=99;
	public int j=print("j");
	
	{
		print("构造快");
	}
	
	static{
		print("静态块");
	}
	
	public Test1(String str){
		System.out.println((++k)+":"+str+" i="+i+" n="+n);
		++i;++n;
	}
	
	public static int print(String str){
		System.out.println((++k)+":"+str+" i="+i+" n="+n);
		++n;
		return ++i;
	}
	
	public static void main(String[] args) {
		Test1 t=new Test1("init");
	}

} 

     这道题当时做的是一塌糊涂。笔试结束忍不住在自己电脑上跑了一下,得到了如下结果:

1:j i=0 n=0
2:构造快 i=1 n=1
3:t1 i=2 n=2
4:j i=3 n=3
5:构造快 i=4 n=4
6:t2 i=5 n=5
7:i i=6 n=6
8:静态块 i=7 n=99
9:j i=8 n=100
10:构造快 i=9 n=101
11:init i=10 n=102

      看到结果时我就懵了,最初学习java时不重视基础的弊端一下子显现了出来。上网查了好多资料,java程序初始化顺序解释比较好的是:当要载入类时(注意只会载入一次),先初始化类里的静态域声明变量为默认值,(即使是赋值了,int值也要为默认的0)然后按顺序的执行静态域和静态块;但是当此类有父类时,要先如果父类有静态域或者静态块,先执行父类的,再执行子类的;当执行完有关静态的东西时,就到了构造类实例时候,会先对实例域初始化为缺省值,然后执行实例块(用{}括起来的部分),然后执行构造方法;当此类有父类时,就应该先进入父类的构造方法执行内容,然后再回到子类构造方法里,然后再跳出子类构造方法,执行子类里的实例域和实例块,然后再回到子类构造方法里,执行子类构造方法里面的内容.(参见http://hi.baidu.com/londalonda/item/3c2b5f722f84bc44ef1e53a7

例如:

public class TestExtend4 {
public static void main(String[] args) {
   C c = new C(); //1
}
}
class A {
   static {
    System.out.println("1"); //2
   }
   public A() { //7
    System.out.println("2"); //8
   }
}

class B extends A {
   static {
    System.out.println("a"); //3
   }
   public B() { //6
    System.out.println("b"); //9
   }
}

class C extends B {
   static {
    System.out.println("I"); //4
   }
   public C() { //5
    System.out.println("II"); //11
   }
  
   {
    System.out.println("yeah!"); //10
   }
}



输出结果为:

1
a
I
2
b
yeah!
II


       但是我所遇到的问题,尽管没有父类,但是初始化情况也很复杂。下面我就说下自己的理解(其中蓝色部分表示程序中的执行语句,黄色背景部分是对应的显示结果部分):

      1、该程序加载完成后,在执行main函数前,先要初始化Test1类里面的静态变量,此时会初始化静态变量为默认值,即有:k=0,t1=null,t2=null,i=0,n=0。之后按照顺序开始执行静态变量的赋值操作有k=0

      2、接下来执行t1=new Test1("t1")。执行该条程序时,需要执行Test1类的构造函数。而按照类成员初始化的顺序,类内部的变量和实例块会在构造器被调用之前得到初始化。因此就需要按照先后顺序先初始化类变量j,再执行构造块,最后执行构造函数。执行public int j=print("j");由于print方法被重写,所以得到输出结果1:j i=0 n=0此处的i=0和n=0正好证明了静态变量在加载时只是完成了初始化,按照顺序还没有进行赋值),此时变为i=1,n=1。接着执行构造块部分,得到输出结果2:构造快 i=1 n=1此时变为i=2,n=2。最后执行Test1的构造函数,得到输出结果3:t1 i=2 n=2,此时变为i=3,n=3。

      3、现在执行t2=new Test1("t2")。执行该条程序时,步骤同上一步,在构造函数调用之前,再次调用类内部的变量和实例块。因而陆续执行了public int j=print("j");和实例块部分。得到输出4:j i=3 n=3,此时变为i=4 n=4。5:构造快 i=4 n=4此时变为i=5 n=5。6:t2 i=5 n=5,此时变为i=6 n=6。此处,特别注意,每次调用类的构造方法之前,该类的内部变量和实例块都会得到调用,验证如下:

public class Test5 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ObjA a1=new ObjA();
		ObjA a2=new ObjA();
		ObjA a3=new ObjA();
	}
}

class ObjA{
	{
		System.out.println("ObjA中实例块被执行");
	}
	static {
		System.out.println("ObjA中静态块被执行");
	}
	public ObjA(){
		System.out.println("constructor in ObjA");
	}
}

得到输出结果为:

ObjA中静态块被执行
ObjA中实例块被执行
constructor in ObjA
ObjA中实例块被执行
constructor in ObjA
ObjA中实例块被执行
constructor in ObjA

     4、下面执行i=print("i")。得到输出7:i i=6 n=6,此时变为i=7 n=7。

     5、下面执行n=99此时变为i=7 n=99。

     6、接着,执行静态块static{ print("静态块"); }。在执行main()函数前,还有执行静态块部分,因而输出8:静态块 i=7 n=99。然后变为i=8 n=100。

     7、现在开始执行main()函数。通过1~6步,初始化完成,执行main函数。执行Test1 t=new Test1("init");按照之前所说,类的构造方法调用之前,还要先调用类内部变量和实例块。因而顺序执行public int j=print("j");实例块代码和构造函数。得到结果9:j i=8 n=100。然后有i=9 n=101。运行实例块得到结果10:构造快 i=9 n=101。同时又i=10 n=102。运行构造函数得到结果11:init i=10 n=102

      至此程序完毕。从上不难发现,在每次构造函数运行前,类的实例变量和实例块部分都会得到执行,且执行顺序服从先后顺序。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值