java程序的加载过程
昨天笔试阿里有个求java程序加载过程的题目很是复杂,回来研究了好久才有点明白,整理一下。原题代码如下,判断输出:
public class StaticTest {
public static int k=0;
public static StaticTest s1=new StaticTest("s1");
public static StaticTest s2=new StaticTest("s2");
public static int i=print("i");
public static int n=99;
public int j=print("j"); {
print("构造块");
} static
{
print("静态块");
} public static int print(String s)
{
System.out.println(++k+":"+s+"\ti="+i+"\tn="+n);
++n;
return ++i;
} public StaticTest(String s)
{
System.out.println(++k+":"+s+"\ti="+i+"\tn="+n);
++i;
++n;
} public static void main(String[] args) {
new StaticTest("init");
}
}
首先给出代码输出:
1:j i=0 n=0
2:构造块 i=1 n=1
3:s1 i=2 n=2
4:j i=3 n=3
5:构造块 i=4 n=4
6:s2 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
没想到只是创建了一个对象,居然执行了这么多语句!下面我们逐条分析每条输出语句。
首先我们需要对java程序的加载过程有个大概的了解:第一执行类中的静态代码,包括静态成员变量的初始化和静态语句块的执行;第二执行类中的非静态代码,包括非静态成员变量的初始化和非静态语句块的执行,最后执行构造函数。在继承的情况下,会首先执行父类的静态代码,然后执行子类的静态代码;之后执行父类的非静态代码和构造函数;最后执行子类的非静态代码和构造函数。用图表示如下:
第一条语句打印的是j相关的内容,所以执行了第7行代码。很明显该行代码执行的是非静态变量的赋值操作,这似乎不符合上述java程序加载规则。我们按照前述规则执行一下代码,首先会执行静态变量k的赋值,然后创建该类的一个静态实例。这时我们就会发现,第一条打印语句可能和该类的这个静态实例对象有关。我们尝试着创建这个静态实例,这时的程序加载过程又变为上述标准的加载过程:首先执行静态代码,然后非静态,最后构造函数。由于静态代码的执行是按代码的先后顺序进行,所以创建该静态实例时只有第一个静态变量k会赋值,后面的静态变量和静态语句块还都不存在;之后执行非静态代码,第一句非静态代码即是代码第7行的变量j赋值。这就解释了为什么第一条打印语句会是第7行的代码。同时这也解释了第二和第三条打印,第二条打印语句执行非静态代码,执行之后就调用构造函数创建实例对象s1。
同理,第4到6条打印语句是在创建静态实例对象s2时执行的。
在完成两个静态实例对象的创建后,下面要执行静态变量i的赋值,这就是第7条打印语句。后面还会对静态成员变量n赋值。之后是执行静态语句块,打印第8行语句。执行完静态代码部分后,接下来要执行非静态代码部分,按照写代码的前后顺序先为j赋值,然后执行非静态语句块,这就是第9和10行的打印语句。在执行完上面的所有步骤之后,开始执行类的构造函数创建对象,这就是第11行打印语句。
通过上面的分析我们发现:上述代码的执行顺序依旧符合一开始说明的java程序加载过程。只是由于有两个该类的静态实例变量,导致打印语句的复杂化。在这种情况下,打印过程类似于一个递归,每一次递归都按照标准的加载过程执行。
该代码一开始给人很多疑问,感觉运行过程中会抛出各种异常,但是代码却神奇地打印出了11条语句,的确让人吃惊。第一个疑问是该类内部有一个该类自身的静态对象,是否会导致循环递归。大家可以尝试一下,将代码第3或4行的static去掉,然后运行程序,就会提示StackOverflowError异常。为啥静态对象不会导致栈溢出,而非静态对象就会溢出?这是因为静态成员变量属于类所有,所有的类对象共享该静态成员变量,也即该静态成员变量只有一份,所以在递归的过程中,当发现正在创建该静态变量时,系统不会再去创建该变量,所以不会递归。但是如果是非静态对象,在递归的过程中,每次遇到该new语句都会再次创建一个新的对象,导致栈溢出。
该代码中另一个疑问是变量i的初值。在静态函数print中会打印i的值,但是在打印的时候变量i可能还没有定义(前6行打印语句都没有定义变量i)。程序居然也给通过了,这说明静态变量的声明会在初始化之前完成,并赋初值0。
java程序的加载过程的更多相关文章
- Java类的加载过程-重点!!
java类的加载过程有以下几步共同完成: 加载->连接->初始化.连接又分为验证.准备.解析 一个非数组类的加载阶段(加载阶段获取类的二进制字节流的动作)是可控性最强的阶段,这一步我们可以 ...
- JAVA - 类的加载过程
JAVA - 类的加载过程 JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化. 加载 加载是类加载过程中的一个阶段,这个阶段会在内存中生成一个代表这个类的java.lang.Class对象 ...
- 动态符号链接的细节 与 linux程序的加载过程
转: http://hi.baidu.com/clivestudio/item/4341015363058d3d32e0a952 值得玩味的一篇分析程序链接.装载.动态链接细节的好文档 导读: by ...
- IOS 应用程序启动加载过程(从点击图标到界面显示)
今天帮同事解决问题的时候发现,程序BUG是由加载过程引起的.所以当局部代码没有问题,但是程序一运行却总不是我们想要结果的时候,我们应该想想是不是因为我们忽略了试图加载过程的原因.下面我们用一个例子来简 ...
- Java 类的加载过程(阿里面试题)
问以下程序打印出什么内容: 问题及解析如下: /** * 加载方法不等于执行方法,初始化变量则会赋值 * 类加载顺序应为 加载静态方法-初始化静态变量-执行静态代码块 * 实例化时 先加载非静态方法- ...
- java中类的加载过程和对象的创建过程
1.类加载过程 首先,jvm在执行时,遇到一个新的类,会先去内存的方法区中去寻找该类的.class文件,如果找到了就直接运行,如果没有找到,则会去硬盘中去寻找该类的.class文件,并将该类文件加载到 ...
- Java类的加载过程与ClassLoader的理解及测试
当程序准备运行某个类,但该类还未被加载到内存中时,会经过以下三个步骤进行类的加载: 类的加载(Load)→类的连接(Link)→类的初始化(Initialize) 加载:类经过javac.exe编译的 ...
- java类的加载过程
1.类的加载顺序 (1)JVM在首次加载类时会对 静态初始化块.静态成员变量. 静态方法进行一次初始化. (2)只有在调用new方法时才会创建类的实例. (3)对象创建过程: 首先执行父类(如果有) ...
- iOS程序的加载过程
1.执行main函数2.执行UIApplicationMain函数1> 创建一个UIApplication对象(UIApplication是整个程序的象征)一个应用只有一个application ...
随机推荐
- Dynamics CRM2016 Web API获取实体元数据Picklist属性的Text&Value
通过组织服务中获取实体picklist字段的text和value可以通过RetrieveAttributeRequest实现,但在使用web api的今天该怎么实现,本文即来一探究竟,本篇基于SDK中 ...
- Android图表库MPAndroidChart(十一)——多层级的堆叠条形图
Android图表库MPAndroidChart(十一)--多层级的堆叠条形图 事实上这个也是条形图的一种扩展,我们看下效果就知道了 是吧,他一般满足的需求就是同类数据比较了,不过目前我还真没看过哪个 ...
- JVM内存区域划分(JDK6/7/8中的变化)
前言 Java程序的运行是通过Java虚拟机来实现的.通过类加载器将class字节码文件加载进JVM,然后根据预定的规则执行.Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同 ...
- HTML简单使用
HTML简单使用 标签 : 前端技术 HTML HTML(Hypertext Marked Language), 即超文本标记语言,能够独立于各种操作系统平台(如UNIX/Linux/Windows等 ...
- RxJava(01-介绍与初体验)
转载请标明出处: http://blog.csdn.net/xmxkf/article/details/51612415 本文出自:[openXu的博客] 目录: 一 简介 二 简单使用 初步探索 代 ...
- iOS开发之UIWebView的常见一些用法
虽然现在Xcode8已经开始使用WKWebView这个框架进行网页展示,但是UIWebView也有一些常用的方法需要知道,下面就简单展示一下,仅供大家参考 相关知识:1.设置背景透明:2.加载本地HT ...
- JAVA进阶之旅(一)——增强for循环,基本数据类型的自动拆箱与装箱,享元设计模式,枚举的概述,枚举的应用,枚举的构造方法,枚举的抽象方法
JAVA进阶之旅(一)--增强for循环,基本数据类型的自动拆箱与装箱,享元设计模式,枚举的概述,枚举的应用,枚举的构造方法,枚举的抽象方法 学完我们的java之旅,其实收获还是很多的,但是依然还有很 ...
- OpenMP与MPI联合编程
研究一下如何一起使用mpi和openmp 先上程序: #include <stdio.h> #include <stdlib.h> #ifdef _OPENMP #includ ...
- java虚拟机 jvm 局部变量表实战
java局部变量表是栈帧重要组中部分之一.他主要保存函数的参数以及局部的变量信息.局部变量表中的变量作用域是当前调用的函数.函数调用结束后,随着函数栈帧的销毁.局部变量表也会随之销毁,释放空间. 由于 ...
- 剑指offer面试题4 替换空格(c)