zoukankan      html  css  js  c++  java
  • Java类、实例的初始化顺序

    题目

    求如下 java 代码的输出:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    
    class T  implements Cloneable{
      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");
      }
    }
    

    分析

    代码主要考察的是类、变量初始化的顺序。

    一般的,我们很清楚类需要在被实例化之前初始化,而对象的初始化则是运行构造方法中的代码。

    本题的代码显然没有这么简单了。本题中涉及到了static {…} 和 {…}这种形式的代码块,以及在类的静态变量中初始化该类的对象这种交错的逻辑,容易让人焦躁(类似于密集恐惧症吧=()。实际上,按照类的装载、链接和初始化逻辑,以及对象初始化的顺序来思考,不难得到答案。

    代码组成

    • 成员变量 2~6 行的变量是 static 的,为类 T 的静态成员变量,需要在类加载的过程中被执行初始化;第 8 行的int j则为实例成员变量,只再类被实例化的过程中初始化。

    • 代码段 9~11 行为实例化的代码段,在类被实例化的过程中执行;13~15 行为静态的代码段,在类被加载、初始化的过程中执行。

    • 方法 方法public static int print(String str) 为静态方法,其实现中牵涉到 k,i,n 三个静态成员变量,实际上,这个方法是专门用来标记执行顺序的方法;T 的构造方法是个实例化方法,在 T 被实例化时调用。

    • main 方法 main 方法中实例化了一个 T 的实例。

    执行顺序分析

    在一个对象被使用之前,需要经历的过程有:类的装载 -> 链接(验证 -> 准备 -> 解析) -> 初始化 -> 对象实例化。(详情参见《Java 类的装载、链接和初始化》),这里需要注意的点主要有:

    • 在类链接之后,类初始化之前,实际上类已经可以被实例化了。

      就如此题代码中所述,在众多静态成员变量被初始化完成之前,已经有两个实例的初始化了。实际上,此时对类的实例化,除了无法正常使用类的静态承运变量以外(还没有保证完全被初始化),JVM 中已经加载了类的内存结构布局,只是没有执行初始化的过程。比如第 3 行public static T t1 = new T("t1");,在链接过程中,JVM 中已经存在了一个 t1,它的值为 null,还没有执行new T("t1")。又比如第 5 行的public static int i = print("i");,在没有执行初始化时,i 的值为 0.

    • 先执行成员变量自身初始化,后执行static {…}{…}代码块中的内容。

      如此策略的意义在于让代码块能处理成员变量相关的逻辑。如果不使用这种策略,而是相反先执行代码块,那么在执行代码块的过程中,成员变量并没有意义,代码块的执行也是多余。

    • 类实例化的过程中,先执行隐式的构造代码,再执行构造方法中的代码 这里隐式的构造代码包括了{}代码块中的代码,以及实例成员变量声明中的初始化代码,以及父类的对应的代码(还好本题中没有考察到父类这一继承关系,否则更复杂;))。为何不是先执行显示的构造方法中的代码,再执行隐式的代码呢?这也很容易解释:构造方法中可能就需要使用到实例成员变量,而这时候,我们是期待实例变量能正常使用的。

    有了如上的分析,也就能推到出最终的输出结果了。实际上,这几个原则都不需要死记硬背,完全能通过理解整个 JVM 的执行过程来梳理出思路的。

    答案

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    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
    

    参考:

  • 相关阅读:
    系统维护相关问题
    Python环境维护
    哈希表解决字符串问题
    论文笔记二:《A Tutoral on Spectral Clustering》
    论文笔记之哈希学习比较--《Supervised Hashing with Kernels》《Towards Optimal Binary Code Learning via Ordinal Embedding》《Top Rank Supervised Binary Coding for Visual Search》
    Java中String、StringBuffer、StringBuilder的比较与源 代码分析
    浙大pat1040 Longest Symmetric String(25 分)
    浙大pat1039 Course List for Student(25 分)
    浙大pat---1036 Boys vs Girls (25)
    百炼oj-4151:电影节
  • 原文地址:https://www.cnblogs.com/dengjiali/p/3490830.html
Copyright © 2011-2022 走看看