您的位置:首页 > 编程语言 > Java开发

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

2013-09-25 22:14 387 查看
     本人菜鸟一只,断断续续学习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。

      至此程序完毕。从上不难发现,在每次构造函数运行前,类的实例变量和实例块部分都会得到执行,且执行顺序服从先后顺序。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐