由阿里巴巴一道笔试题看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函数里面的部分,依次调用动态代码块和构造函数,不再赘述。