本文再也不以ClassLoader的视角解释这些问题。程序员
首先,Java代码有个特色,就是成员变量能够在前面的方法中使用,在后面定义。这一特性,不少人说Java了不得,但是为何呢?Java为什么可以这样呢?面试
咱们首先来看一道面试题: 3d

写出上面代码的运行结果。
其实对于Java了解比较深刻的人,不屑于解决这道题,由于看代码写运行结果,再常规不过,但是这个题,要是写准了,还真的不容易,由于,咱们要以相似C语言的视角审视这道题。
OK,咱们首先理解一个基本概念,内存空间必定是先申请,再初始化,再使用的。例如C语言中的malloc分配空间,而后初始化写0,而后使用。其实Java也是如此的。
首先,咱们从main方法入手,只有一行new对象(第26行)。
new对象首先要加载类,这是确定的了,所以“Test”须要加载类,也就是加载Test的字节码文件到方法区(永久代)中。这时须要把类中的“static”部分处理完成。
也就是说,static部分是随着Test的字节码文件进入到永久代的。
它的过程是:先把全部的static部分申请空间,而后再给每个static成员由上至下分配初始值。初始值有两种,一种是Java对于基本数据类型的默认初始值,这个默认初始值在申请空间之时就给每个成员赋予了,另外一种是Java程序员利用“=”对成员进行赋初始值。
如图所示,五个静态成员在永久代首先被分配内存。
此时,k=0,t1=null,t2=null,i=0,n=0。空间申请完成以后,咱们把0赋值给k,虽然k被分配内存以后就是0,可是依然还要把0再赋值给k,由于“=”右边的“0”是程序员对k的赋值。而后是给t1实例一个对象,也就是t1本来是null,如今把new出来的对象赋值给t1,因而就要构建一个Test对象。构建对象时,要把全部的非static部分初始化一份,放入堆内存。
这时,你就理解了Java语法中,为何静态成员是“类名.成员”,而非静态成员是“对象.成员”了。由于所属关系不一样。
那么咱们开始找非静态的成员
如图,有j和一个构造块。
所以是先给j申请空间,而后运行print("j")方法,把方法的返回值交给j。
因而,这个程序的第一段打印结果出来了:
打印:
此后,k=1 , n=1 , i=1。
而后是接着找非静态的部分,就只有构造块了,所以是:
此后,k=2 , n=2 , i=2。
这时,咱们构造对象的准备工做作完了,也就是非静态的代码都执行完了,所以开始实例化对象,Java实例化对象使用构造方法,所以执行:
打印:
此后,t1再也不是null,而后初始化t2,过程和t1同样,所以运行结果是:
再而后初始化静态的i,所以是执行:
输出打印:
而后初始化 n(第6行),直接把n赋值为99。可是什么都不打印。
而后再往下是静态块:
输出打印:
至此,全部的静态部分也都初始化完毕了,能够new Test("init")了:
输出打印:
因此整体打印以下:
总结,其实Java自己也是代码从上往下走的,只不过静态部分和非静态部分在两个次元里。Java的成员有分配空间,赋默认值两个过程,且首先为全体成员申请空间,而后由上至下逐一赋值。