从阿里巴巴笔试题看Java加载顺序
一、阿里巴巴笔试题:
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”)的实例化过程 --- 静态变量的初始化可以在声明处进行,也可以在静态代码块进行
- 声明实例变量j,并设置变量初始化的值0,然后进入构造方法public T (String str)构造方法执行完毕,将heap区中创建的T的实例对象,并赋值给t1
- 执行静态方法print("j"),并将返回值赋值给j,print()方法输出“1:j i=0 n=0”,返回值为1;并完成j=1的赋值
- 执行静态方法print("构造块"),print()方法输出“2:构造快 i=1 n=1”,返回值为2
- 执行构造方法的剩下三行,即
System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);
++n;
++i;
# 此时str的值是构造方法的传参“t1”,最终输出“3:t1 i=2 n=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实现,在首次主动使用某类的时候才会加载该类。
被动使用:
- 父接口并不会因为它的子接口或者实现类的初始化而初始化. 只有当程序首次使用特定接口的静态变量时,才会导致该接口的初始化.
- 通过数组定义来引用类,不会触发类的初始化,如SubClass[] sca = new SubClass[10];
- Java程序中对类的编译时常量的使用,被看做是对类的被动使用,不会导致类的初始化.(对于final类型的静态变量,如果在编译时就能计算出变量的取值,那么这种变量被看做编译时常量.)
- 如果调用子类的静态变量是从父类继承过来并没有覆写的,那么也就相当于只用到了父类的静态变量,和子类无关,所以这个时候子类不需要进行类初始化)。
自增运算:
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 Tutor - Visualize Java code execution to learn Java online (also visualize Python, Java, JavaScript, TypeScript, Ruby, C, and C++ code)
从阿里巴巴笔试题看Java加载顺序的更多相关文章
- 由阿里巴巴笔试题看java加载顺序
一.阿里巴巴笔试题: public class T implements Cloneable { public static int k = 0; public static T t1 = new T ...
- 由阿里巴巴一道笔试题看Java静态代码块、静态函数、动态代码块、构造函数等的执行顺序
一.阿里巴巴笔试题: public class Test { public static int k = 0; public static Test t1 = new Test("t1&qu ...
- (转)面试题--JAVA中静态块、静态变量加载顺序详解
public class Test { //1.第一步,准备加载类 public static void main(String[] args) { new Test(); //4.第四步,new一个 ...
- 【笔试题】Java笔试题知识点
Java高概率笔试题知识点 Java语法基础部分 [解析]java命令程序执行字节码文件是,不能跟文件的后缀名! 1.包的名字都应该是由小写单词组成,它们全都是小写字母,即便中间的单词亦是如此 2.类 ...
- Java加载资源文件的两种方法
处理配置文件对于Java程序员来说再常见不过了,不管是Servlet,Spring,抑或是Structs,都需要与配置文件打交道.Java将配置文件当作一种资源(resource)来处理,并且提供了两 ...
- java中静态变量,静态代码块,静态方法,实例变量,匿名代码块等的加载顺序
转自:http://blog.csdn.net/mrzhoug/article/details/51581994 一.在Java中,使用”{}”括起来的代码称为代码块,代码块可以分为以下四种: 1.普 ...
- java中带继承类的加载顺序详解及实战
一.背景: 在面试中,在java基础方面,类的加载顺序经常被问及,很多时候我们是搞不清楚到底类的加载顺序是怎么样的,那么今天我们就来看看带有继承的类的加载顺序到底是怎么一回事?在此记下也方便以后复习巩 ...
- Java 类中各成分加载顺序 和 内存中的存放位置
参加一个笔试,有一个关于类的静态代码块.构造代码块.构造函数的执行顺序的问题.不太清楚,网上百度了一下.在这里记录一下. 一.什么时候会加载类?使用到类中的内容时加载:有三种情况1.创建对象:new ...
- Java 中类的加载顺序
如果类A和类B中有静态变量,静态语句块,非静态变量,非静态语句块,构造函数,静态方法,非静态方法,同时类A继承类B,请问当实例化A时,类内部的加载顺序是什么? 测试代码如下: Class B: pub ...
随机推荐
- 【原】AFNetworking源码阅读(四)
[原]AFNetworking源码阅读(四) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇还遗留了很多问题,包括AFURLSessionManagerTaskDe ...
- scrapy 知乎用户信息爬虫
zhihu_spider 此项目的功能是爬取知乎用户信息以及人际拓扑关系,爬虫框架使用scrapy,数据存储使用mongo,下载这些数据感觉也没什么用,就当为大家学习scrapy提供一个例子吧.代码地 ...
- Angular企业级开发(4)-ngResource和REST介绍
一.RESTful介绍 RESTful维基百科 REST(表征性状态传输,Representational State Transfer)是Roy Fielding博士在2000年他的博士论文中提出来 ...
- C++随笔:.NET CoreCLR之GC探索(1)
一直是.NET程序员,但是.NET的核心其实还是C++,所以我准备花 一点时间来研究CoreCLR和CoreFX.希望这个系列的文章能给大家带来 帮助. GC的代码有很多很多,而且结构层次对于一个初学 ...
- android手机登录时遇到“QQ安全登录发现病毒”解决
android手机作为开源系统非常容易感染病毒,有时候我们会经常遇到手机QQ登录时检测到app被感染,一般情况是由手机感染病毒所引起的,安装腾讯管家后只能检测病毒和卸载感染病毒的软件,不能清除病毒.解 ...
- jQuery radio的取值与赋值
取值: $("input[name='radioName']:checked").val(); 赋值: $("input[name='radioName'][value= ...
- Winserver2012下mysql 5.7解压版(zip)配置安装
一.安装 下载mysqlzip版本mysql不需要运行可执行文件,解压即可,下载zip版本mysqlmsi版本mysql双击文件即可安装,相对简单,本文不介绍此版本安装 配置环境变量打开环境变量配置页 ...
- 萌新笔记——linux下查看内存的使用情况
windows上有各种软件可以进行"一键加速"之类的操作,释放掉一些内存(虽然我暂时不知道是怎么办到的,有待后续学习).而任务管理器也可以很方便地查看各进程使用的内存情况,如下图: ...
- Linux测试环境搭建的学习建议
随着Linux应用的扩展许多朋友开始接触Linux,根据学习Windwos的经验往往有一些茫然的感觉:不知从何处开始学起.这里介绍学习Linux测试环境搭建的一些建议. 一.Linux测试环境搭建从基 ...
- 如何让spring mvc web应用启动时就执行特定处理
Asp.Net的应用中通过根目录下的Global.asax,在Application_Start方法中做一些初始化操作,比如:预先加载缓存项对网站热点数据进行预热,获取一些远程的配置信息等等. Spr ...