一、阿里巴巴笔试题:

public 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");
    }

}

当.java源代码转换成一个.class文件后,其转换成类似下面的等价代码:

public class T implements Cloneable {
    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");
    }

}

二、运行结果

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

三、加载过程分析

一、执行main()时,由于使用new语句创建实例,属于首次主动使用类T,JVM加载类T,

声明静态变量k、t1、t2、i、n(为静态变量分配内存),并设置变量初始化的值(0,null,null,0,0)  --- 类生命周期的准备阶段

在static代码块中对k进行第二次赋值,k=0;

在static代码块中对t1进行第二次赋值,触发new T(“t1”)的实例化过程                                           --- 静态变量的初始化可以在声明处进行,也可以在静态代码块进行

  1. 声明实例变量j,并设置变量初始化的值0,然后进入构造方法public T (String str)构造方法执行完毕,将heap区中创建的T的实例对象,并赋值给t1
    1. 执行静态方法print("j"),并将返回值赋值给j,print()方法输出“1:j   i=0   n=0”,返回值为1;并完成j=1的赋值
    2. 执行静态方法print("构造块"),print()方法输出“2:构造快   i=1   n=1”,返回值为2
    3. 执行构造方法的剩下三行,即
       System.out.println((++k) + ":" + str + "    i=" + i + "  n=" + n);
              ++n;
              ++i;
      # 此时str的值是构造方法的传参“t1”,最终输出“3:t1 i=2 n=2”
  2. 在static代码块中对t2进行第二次赋值,触发new T(“t2”)的实例化过程,由于k,i,n是静态变量,自增操作的值都被保留了下来。输出: 
4:j   i=3   n=3
5:构造快   i=4   n=4
6:t2    i=5  n=5

执行语句 i = print(“i”),输出

7:i   i=6   n=6

# print(“i”)返回值为6,赋值给i

执行n=99;print(“静态块”),输出

8:静态块   i=7   n=99

#因为n=99的赋值语句,输出成为了n=99

至此,类初始化完毕。

二、完成类T的加载后,然后进行new T("init")的创建类T的实例,

过程和前几次new T("t1")、new T("t2")基本相同,输出

9:j   i=8   n=100
10:构造快   i=9   n=101
11:init    i=10  n=102

实例化操作3次 * 3 + print(“i”) + print(“静态块”),共计输出11行内容

四、涉及知识点

主动使用:

1):最为常用的new一个类的实例对象,或者通过反射/克隆/反序列化手段来创建实例。
2):直接调用类的静态方法(也包括main方法)。
3):操作(访问或改变)该类或接口中声明的非编译期常量静态字段(对于final类型的静态变量,如果在编译时就能计算出变量的取值,那么这种变量被看做编译时常量,不视作主动使用
4):调用Java API的某些反射方法,比如调用Class.forName("ClassName")
5):初始化一个类的子类的时候,父类也相当于被程序主动调用了(如果调用子类的静态变量是从父类继承过来并没有覆写的,那么也就相当于只用到了父类的静态变量,和子类无关,所以这个时候子类不需要进行类初始化)。
6):JVM虚拟机启动时被标为启动类的类.

所有的JVM实现,在首次主动使用某类的时候才会加载该类。

被动使用:

  1. 父接口并不会因为它的子接口或者实现类的初始化而初始化. 只有当程序首次使用特定接口的静态变量时,才会导致该接口的初始化.
  2. 通过数组定义来引用类,不会触发类的初始化,如SubClass[] sca = new SubClass[10];
  3. Java程序中对类的编译时常量的使用,被看做是对类的被动使用,不会导致类的初始化.(对于final类型的静态变量,如果在编译时就能计算出变量的取值,那么这种变量被看做编译时常量.)
  4. 如果调用子类的静态变量是从父类继承过来并没有覆写的,那么也就相当于只用到了父类的静态变量,和子类无关,所以这个时候子类不需要进行类初始化)。

自增运算:

int i = 0; System.out.println(i++); System.out.println(i)    # 输出0和1

int i = 0; System.out.println(i++); System.out.println(i) # 输出1和1

其他:

对于类的成员变量,不管程序有没有显式地进行初始化,JVM都会先自动给它初始化为默认值。
局部变量声明以后,JVM不会自动地为它初始化为默认值,必须先经过显式的初始化才能使用它。如果编译器确认一个局部变量在使用之前可能没有被初始化,编译器将报错。
数组和String字符串都不是基本数据类型,它们被当作类来处理,是引用数据类型。引用数据类型的默认初始值都是null

参考:

Java类、实例的初始化顺序

Java Tutor - Visualize Java code execution to learn Java online (also visualize PythonJavaJavaScriptTypeScriptRubyC, and C++ code)

从阿里巴巴笔试题看Java加载顺序的更多相关文章

  1. 由阿里巴巴笔试题看java加载顺序

    一.阿里巴巴笔试题: public class T implements Cloneable { public static int k = 0; public static T t1 = new T ...

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

    一.阿里巴巴笔试题: public class Test { public static int k = 0; public static Test t1 = new Test("t1&qu ...

  3. (转)面试题--JAVA中静态块、静态变量加载顺序详解

    public class Test { //1.第一步,准备加载类 public static void main(String[] args) { new Test(); //4.第四步,new一个 ...

  4. Java加载资源文件的两种方法

    处理配置文件对于Java程序员来说再常见不过了,不管是Servlet,Spring,抑或是Structs,都需要与配置文件打交道.Java将配置文件当作一种资源(resource)来处理,并且提供了两 ...

  5. java中带继承类的加载顺序详解及实战

    一.背景: 在面试中,在java基础方面,类的加载顺序经常被问及,很多时候我们是搞不清楚到底类的加载顺序是怎么样的,那么今天我们就来看看带有继承的类的加载顺序到底是怎么一回事?在此记下也方便以后复习巩 ...

  6. Java 类中各成分加载顺序 和 内存中的存放位置

    参加一个笔试,有一个关于类的静态代码块.构造代码块.构造函数的执行顺序的问题.不太清楚,网上百度了一下.在这里记录一下. 一.什么时候会加载类?使用到类中的内容时加载:有三种情况1.创建对象:new ...

  7. Java 中类的加载顺序

    如果类A和类B中有静态变量,静态语句块,非静态变量,非静态语句块,构造函数,静态方法,非静态方法,同时类A继承类B,请问当实例化A时,类内部的加载顺序是什么? 测试代码如下: Class B: pub ...

  8. java web项目中classes文件夹下的class和WEB-INF/lib中jar里的class文件加载顺序

    如果是发布到weblogic的话,可以在WebContent\WEB-INF\weblogic.xml里面配置.参考配置如下:<?xml version="1.0" enco ...

  9. Java笔记(十二)&hellip;&hellip;类中各部分加载顺序及存放位置问题

    什么时候会加载类 使用到类中的内容时加载,三种情况: 创建对象:new StaticDemo(); 使用类中的静态成员:StaticCode.num = 9;  StaticCode.getNum() ...

随机推荐

  1. hibernate.cfg.xml

    <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN&q ...

  2. 整理:Javascript获取数组中的最大值和最小值的方法汇总

    方法一: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 //最小值 Array.prototype.min = function ...

  3. EMC学习之电磁辐射

    我们在接触新鲜事物的时候,通常习惯用自己熟悉的知识去解释自己不熟悉的事物.EMC知识更多的涉及到微波和射频,对于像我这种专注于信号完整性而 对EMC知识知之甚少的菜鸟来说,最初也只能用SI的一些基础知 ...

  4. boost之lexical_cast

    第一次翻译,虽然是个很简单的函数介绍... 文件boost/lexical_cast.hpp中定义了此函数: namespace boost { class bad_lexical_cast; tem ...

  5. Linux pthread

    #include <stdio.h> #include <stdlib.h> #include <string.h> #include <pthread.h& ...

  6. SQLServer字符操作

    1.CHARINDEX('A',‘VALUE’)    result:2 style:PATINDEX(varchar,varchar) 解释:A在字符串VALUE的位置次序. 2.PATINDEX( ...

  7. 虚拟机VM安装linux系统

    废话不多说,直接上图文过程: 1.首先是下载linux镜像文件了(CentOS,Ubuntu等,根据自己的实际需求下载) linux镜像下载(提供几个32位的linux镜像下载,如有其他需求请自行百度 ...

  8. Nginx--&gt;基础--&gt;理论--&gt;001:Nginx基本介绍

    一.nginx基本介绍 传统上基于进程或者线程模型架构的web服务通过每进程或者每线程处理并发连接请求,这势必毁在网络和I/O操作时产生阻塞,其另外一个必然结果则是对内存和CPU的利用率低下,产生一个 ...

  9. requirejs解决异步模块加载方案

    他首先会遍历enableRegistry取出其中定义的模块,并且将没有加载成功的模块标识注入noLoads数组,如果过期了这里就会报错 如果上述没问题还会做循环依赖的判断,主要逻辑在breakCycl ...

  10. Trasformation中的公式报错误,错误数据行定位

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...