由阿里巴巴一道笔试题看Java静态代码块、静态函数、动态代码块、构造函数等的执行顺序

一、阿里巴巴笔试题:

public class Test {
	public static int k = 0;
	public static Test t1 = new Test("t1");
	public static Test t2 = new Test("t2");
	public static int i = print("i");
	public static int n = 99;
	private int a = 0;
	public int j = print("j");
	
	{
		print("构造块");
	}

	static {
		print("静态块");
	}

	public Test(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[]) {
		Test t = new Test("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

二、我们暂且先不看这道题,先回忆一下代码块、构造函数执行顺序的基本知识:

总体规则:静态代码块 -> 动态代码块 ->构造函数

静态代码块只在第一次new的时候执行一次,之后不再执行;动态代码块在每次new的时候都执行一次。

在不涉及继承的情况下:

1.静态代码块和静态成员变量在加载代码时执行,只执行一次,按照它们出现的顺序先后执行;

2.动态代码块在每次实例化对象时执行,在构造函数之前执行,多个动态代码块按照它们出现的顺序先后执行;

在涉及继承的情况下:

1.执行父类的静态代码块和静态成员变量定义,执行子类的静态代码块和静态成员变量定义;

2.执行父类的动态代码块,执行父类的构造函数;

3.执行子类的动态代码块,执行子类的构造函数;

4.如果父类构造函数中用到的函数被子类重写,那么在构造子类对象时调用子类重写的方法;

代码:

public class staticTest {
	public static void main(String[] args) {
		A a1 = new B();
	}
}

class A{
	public A(){
		System.out.println("A constructor.");
		func();
	}
	
	static{
		System.out.println("class A static block.");
	}
	
	private int ai = getAi();

	{
		System.out.println("class A dynamic block.");
	}
	
	private static int asi = getAsi();
	
	private int getAi(){
		System.out.println("class A dynamic int.");
		return 1;
	}
	
	private static int getAsi(){
		System.out.println("class A static int.");
		return 0;
	}
	
	public void func(){
		System.out.println("A.func()");
	}
}


class B extends A{
	public B(){
		System.out.println("B constructor.");
		func();
	}
	
	static{
		System.out.println("class B static block.");
	}
	
	private int bi = getBi();
	
	{
		System.out.println("class B dynamic block.");
	}
	
	private static int bsi = getBsi();
	
	private int getBi(){
		System.out.println("class B dynamic int.");
		return 1;
	}
	
	private static int getBsi(){
		System.out.println("class B static int.");
		return 0;
	}
	
	public void func(){
		System.out.println("B.func()");
	}
}


输出:

class A static block.
class A static int.
class B static block.
class B static int.
class A dynamic int.
class A dynamic block.
A constructor.
B.func()
class B dynamic int.
class B dynamic block.
B constructor.
B.func()


三、对阿里巴巴笔试题的分析

public static int k = 0;
public static Test t1 = new Test("t1");


函数先执行到这里,在构造t1的过程中发生了什么呢,通过对程序打断点分析,我发现,程序并没有执行其中的静态代码块,而是执行非静态代码块,为什么呢?我的理解是,“静态代码块只在程序加载的时候运行,并且是按其出现顺序加载的”,而现在我们在构造一个新对象,属于程序加载的时候的一个分支,然后还会走回来继续加载剩下的未加载的静态代码块。所以在这次创建静态对象的过程中,之后执行其中的非静态代码块。

下面我们看到的两个*****中间的就是在执行该语句的过程中产生的分支:

**********

所以接下来执行的是:

private int a = 0;
public int j = print("j");

执行第二句的时候会调用

public static int print(String str) {
	System.out.println((++k) + ":" + str + "    i=" + i + "     n=" + n);
	++n;
	return ++i;
}


然后执行动态代码块:

{
	print("构造块");
}


然后调用构造函数:

public Test(String str) {
	System.out.println((++k) + ":" + str + "    i=" + i + "     n=" + n);
	++i;
	++n;
}


这个顺序就是“动态代码块->构造函数"。

*************

然后跳出该分支,继续加载静态代码块:

public static Test t2 = new Test("t2");


执行此句会重复上面两个********之间的分支,这里不再赘述。

然后是:

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

static {
	print("静态块");
}


最后执行main函数里面的部分,依次调用动态代码块和构造函数,不再赘述。





原文地址:https://www.cnblogs.com/suncoolcat/p/3357994.html