疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

Java程序的加载过程


 

   

  [java] view

  plain copy

  print?

  public class StaticTest {

  public static int k=0;

  public static StaticTest s1=new StaticTest("s1");

  public static StaticTest s2=new StaticTest("s2");

  public static int i=print("i");

  public static int n=99;

  public int j=print("j");

  {

  print("构造块");

  }

  static

  {

  print("静态块");

  }

  public static int print(String s)

  {

  System.out.println(++k+":"+s+" i="+i+" n="+n);

  ++n;

  return ++i;

  }

  public StaticTest(String s)

  {

  System.out.println(++k+":"+s+" i="+i+" n="+n);

  ++i;

  ++n;

  }

  public static void main(String[] args) {

  new StaticTest("init");

  }

  }

  首先给出代码输出:

  [plain] view

  plain copy

  print?

  1:j i=0 n=0

  2:构造块 i=1 n=1

  3:s1 i=2 n=2

  4:j i=3 n=3

  5:构造块 i=4 n=4

  6:s2 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程序的加载过程有个大概的了解:第一执行类中的静态代码,包括静态成员变量的初始化和静态语句块的执行;第二执行类中的非静态代码,包括非静态成员变量的初始化和非静态语句块的执行,最后执行构造函数。在继承的情况下,会首先执行父类的静态代码,然后执行子类的静态代码;之后执行父类的非静态代码和构造函数;最后执行子类的非静态代码和构造函数。用图表示如下:

  第一条语句打印的是j相关的内容,所以执行了第7行代码。很明显该行代码执行的是非静态变量的赋值操作,这似乎不符合上述java程序加载规则。我们按照前述规则执行一下代码,首先会执行静态变量k的赋值,然后创建该类的一个静态实例。这时我们就会发现,第一条打印语句可能和该类的这个静态实例对象有关。我们尝试着创建这个静态实例,这时的程序加载过程又变为上述标准的加载过程:首先执行静态代码,然后非静态,最后构造函数。由于静态代码的执行是按代码的先后顺序进行,所以创建该静态实例时只有第一个静态变量k会赋值,后面的静态变量和静态语句块还都不存在;之后执行非静态代码,第一句非静态代码即是代码第7行的变量j赋值。这就解释了为什么第一条打印语句会是第7行的代码。同时这也解释了第二和第三条打印,第二条打印语句执行非静态代码,执行之后就调用构造函数创建实例对象s1。

  同理,第4到6条打印语句是在创建静态实例对象s2时执行的。

  在完成两个静态实例对象的创建后,下面要执行静态变量i的赋值,这就是第7条打印语句。后面还会对静态成员变量n赋值。之后是执行静态语句块,打印第8行语句。执行完静态代码部分后,接下来要执行非静态代码部分,按照写代码的前后顺序先为j赋值,然后执行非静态语句块,这就是第9和10行的打印语句。在执行完上面的所有步骤之后,开始执行类的构造函数创建对象,这就是第11行打印语句。

  通过上面的分析我们发现:上述代码的执行顺序依旧符合一开始说明的java程序加载过程。只是由于有两个该类的静态实例变量,导致打印语句的复杂化。在这种情况下,打印过程类似于一个递归,每一次递归都按照标准的加载过程执行。

  该代码一开始给人很多疑问,感觉运行过程中会抛出各种异常,但是代码却神奇地打印出了11条语句,的确让人吃惊。第一个疑问是该类内部有一个该类自身的静态对象,是否会导致循环递归。大家可以尝试一下,将代码第3或4行的static去掉,然后运行程序,就会提示StackOverflowError异常。为啥静态对象不会导致栈溢出,而非静态对象就会溢出?这是因为静态成员变量属于类所有,所有的类对象共享该静态成员变量,也即该静态成员变量只有一份,所以在递归的过程中,当发现正在创建该静态变量时,系统不会再去创建该变量,所以不会递归。但是如果是非静态对象,在递归的过程中,每次遇到该new语句都会再次创建一个新的对象,导致栈溢出。

  该代码中另一个疑问是变量i的初值。在静态函数print中会打印i的值,但是在打印的时候变量i可能还没有定义(前6行打印语句都没有定义变量i)。程序居然也给通过了,这说明静态变量的声明会在初始化之前完成,并赋初值0。