2013阿里java代码初始化题目笔试题解析

时间:2023-01-16 17:30:24
public class T {
    public static int k = 0;
    public static T t1 = new T("t1");
    public static T t2 = new T("t2");
    public static int i = print("i");
    public static int n = 99;

    public int j = print("j");

    {
        print("构造快");
    }

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

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

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

    public static void main(String[] args) {
        T t = new T("init");
    }
}

可能出现在class文件中的两种编译器产生的方法是:
- 实例初始化方法(名为<init>):负责初始化实例变量与执行该类的构造方法
- 类与接口初始化方法(名为<clinit>):负责初始化类变量与执行静态代码块。

两者的区别见:http://blog.csdn.net/x_iya/article/details/77097118

对于如上的代码,其对应的等价代码如下:

public class T {
    public static int k;
    public static T t1;
    public static T t2;
    public static int i;
    public static int n;

    static {
        k = 0;
        t1 = new T("t1");
        t2 = new T("t2");
        i = print("i");
        n = 99;
        print("静态块");
    }

    public int j;

    public T(String str) {
        j = print("j");
        print("构造快");

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

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

    public static void main(String[] args) {
        T t = new T("init");
    }
}

static代码块即为虚拟机中生成的<clinit> 方法,构造函数即为虚拟机中生成的<init> 方法

以上代码的反汇编结果为:

D:\N3verL4nd\Desktop>javap -c T
Compiled from "T.java"
public class T {
  public static int k;

  public static T t1;

  public static T t2;

  public static int i;

  public static int n;

  public int j;

  public T(java.lang.String);
    Code:
       0: aload_0
       1: invokespecial #1 // Method java/lang/Object."<init>":()V
       4: aload_0
       5: ldc           #2 // String j
       7: invokestatic  #3 // Method print:(Ljava/lang/String;)I
      10: putfield      #4 // Field j:I
      13: ldc           #5 // String 构造快
      15: invokestatic  #3 // Method print:(Ljava/lang/String;)I
      18: pop
      19: getstatic     #6 // Field java/lang/System.out:Ljava/io/PrintStream;
      22: new           #7 // class java/lang/StringBuilder
      25: dup
      26: invokespecial #8 // Method java/lang/StringBuilder."<init>":()V
      29: getstatic     #9 // Field k:I
      32: iconst_1
      33: iadd
      34: dup
      35: putstatic     #9 // Field k:I
      38: invokevirtual #10 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      41: ldc           #11 // String :
      43: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      46: aload_1
      47: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      50: ldc           #13 // String i=
      52: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      55: getstatic     #14 // Field i:I
      58: invokevirtual #10 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      61: ldc           #15 // String n=
      63: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      66: getstatic     #16 // Field n:I
      69: invokevirtual #10 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      72: invokevirtual #17 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      75: invokevirtual #18 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      78: getstatic     #16 // Field n:I
      81: iconst_1
      82: iadd
      83: putstatic     #16 // Field n:I
      86: getstatic     #14 // Field i:I
      89: iconst_1
      90: iadd
      91: putstatic     #14 // Field i:I
      94: return

  static {};
    Code:
       0: iconst_0
       1: putstatic     #9 // Field k:I
       4: new           #19 // class T
       7: dup
       8: ldc           #22 // String t1
      10: invokespecial #21 // Method "<init>":(Ljava/lang/String;)V
      13: putstatic     #23 // Field t1:LT;
      16: new           #19 // class T
      19: dup
      20: ldc           #24 // String t2
      22: invokespecial #21 // Method "<init>":(Ljava/lang/String;)V
      25: putstatic     #25 // Field t2:LT;
      28: ldc           #26 // String i
      30: invokestatic  #3 // Method print:(Ljava/lang/String;)I
      33: putstatic     #14 // Field i:I
      36: bipush        99
      38: putstatic     #16 // Field n:I
      41: ldc           #27 // String 静态块
      43: invokestatic  #3 // Method print:(Ljava/lang/String;)I
      46: pop
      47: return
}

加载过程分析:

1、执行main方法(T.main),导致 T 类的加载。
2、在类的准备阶段(加载、验证、准备、解析、初始化)会为静态变量分配存储空间并且赋初始值。对于上述代码中声明的静态变量k、t1、t2、i、n被初始化为(0,null,null,0,0)
3、在初始化阶段为类变量赋值(用户自定义的值),也就是执行上述等价的static方法。k被第二次赋值,k=0。t1 = new T(“t1”);在对t1进行初始化的过程中触发了<init> 方法的执行。此时不会再去执行<clinit> 方法了。t2也是一样的操作。注意此时对于类变量i与n还都没有进行用户自定义初始化,还都是0。
4、执行i = print(“i”);n = 99;print(“静态块”);
5、以上就完成了<clinit> 的执行,由JVM调用。然后再去执行<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