编写java程序时,每创建一个新的对象,都会对对象的内容进行初始化。
每一个类的方法中的局部变量都有严格的初始化要求,因此假如写出下面的程序:
void f(){
int i;
i++;
}
编译时就会得到一条出错信息,因为java会严格检查方法内部局部变量的初始化,正如《java编程思想》中所说“未初始化的局部变量更有可能是程序员的疏忽”
但是对于类内部的字段,java并不会严格检查字段是否初始化,因为类的每一个基本数据类型成员都保证会有一个初始值,哪怕构造器什么都没做。
其实一个对象的初始化并不仅仅只是简单的调用构造器,在这之前,java已经做了很多工作了。
创建一个新的对象,就相当于苹果树上长出了一个新的苹果。只有在这个苹果长出来的时候,我们才知道其具体的形状,颜色,重量,这是每个苹果所具有的特性,这些特性就相当于对象中的字段。但是所有的苹果同样具有共性,比如说都具有类似的结构,这些性质就相当于对象中的static字段,是所有该类对象所共享的。而构造器,就相当于在采摘下苹果之后,通过人工的手段对其特性做出一些改变。
当java要创建一个新的对象时,首先会查找类路径,找到对应的类,相当于我们种下了一颗苹果树。
此时java会载入这个类,此时类中有关静态初始化的所有动作都会被执行,即类中带有static前缀的字段会被分配内存,然后进行初始化的赋值。无论为这个类new多少个对象,static字段的初始化在整个程序的生命周期中只会进行一次,且有自己独立的内存位置,为所有该类的对象所共享。就像我们知道这棵种下的苹果树会长出的东西叫做苹果,和世界任何一个地方的同类的苹果树一样,虽然苹果还没有长出来,但是它作为苹果所具有的性质是客观存在的。
//例子
如果当前类是导出类,那么java编译器就会从其基类开始加载,分别初始化其中的静态域;
无论如何,static域的初始化都只会进行这一次。
接下来,java编译器就会为该具体的对象分配内存空间。内存被分配的时候,对应的内存空间会被全部清0,所以所有的非static字段的初始值都是0或者与0等价的值,对象的引用都被置为null。这就相当于苹果树的果实刚刚准备生长的状态,一切都是最初的模样。
下一步,java编译器就会利用每个字段的初始化语句对字段进行赋值,如果初始化语句调用了别的对象或者方法,那么编译器也会不知疲倦的去找,就是为了保证在调用构造器之前,字段的初始化语句都执行完成。值得注意的是,类中字段的分配内存空间与赋值的过程是先于构造器以及其他方法进行的,与代码中初始化语句与方法的排列顺序无关,哪怕包括构造器在内的所有方法都排在字段初始化语句之前,编译器也会跳过这些方法,先执行初始化语句。
如果当前类是导出类,那么java编译器就会从其基类开始执行字段的初始化,逐步向外进行,导出类的加载与初始化赋值都必须在基类的基础上进行,不能凭空产生。
这个赋值的过程就相当于苹果树的果实的生长过程,苹果的大小,重量,颜色等特性都是在这个过程中慢慢形成的。
接下来才轮到构造器对对象进行初始化,按照new对象时输入的参数对每个对象进行塑造,这个过程就相当于苹果采摘完成之后,清洗干净,修剪枝叶。
测试代码如下:
public class init_Test {
public static void main(String args[]) {
System.out.println("the first code in main");
}
static apple apple1=new apple();
static apple apple2=new apple();
} class Test{
static int f(String words) {
System.out.println(words);
return 1;
}
} class apple{
int value1 =Test.f("init value1");
static int static1=Test.f("static init static1");
apple(){
System.out.println("constructor of class apple");
System.out.println("check static1 ="+static1);
System.out.println("check static2 ="+static2);
System.out.println("check static3 ="+static3);
System.out.println("check value1 ="+value1);
System.out.println("check value2 ="+value2);
System.out.println("check value3 ="+value3);
}
int value2=Test.f("init value2");
static int static2=Test.f("static init static2");
static int static3;
int value3;
}
产生的输出如下:
static init static1
static init static2
init value1
init value2
constructor of class apple
check static1 =1
check static2 =1
check static3 =0
check value1 =1
check value2 =1
check value3 =0
init value1
init value2
constructor of class apple
check static1 =1
check static2 =1
check static3 =0
check value1 =1
check value2 =1
check value3 =0
the first code in main
接下来让我们捋清楚这段代码的执行过程。
首先进入程序的入口:main方法
要执行main方法,就需要首先加载main方法所在的init_Test类。
加载时会对static域进行初始化,因此控制流会跳过main方法,首先执行下面的两条初始化语句,对静态的apple1和apple2进行初始化。也就是创建两个新的apple对象。
初始化调用了apple方法,因此控制流会转而寻找apple类,这是控制流第一次到达该类,于是就会对其中的静态域进行初始化:
也就是上图所示的static1,static2,static3三个变量。
初始化调用了以下静态方法,定义该方法的目的就是在控制台中打印信息。
注意到控制台中的前两条输出:
===============================================================
完成了类的加载,就开始创建具体的对象了,此时控制流开始为apple1对象创建内存空间,非static域首先被赋值为0,然后执行对应的初始化语句;
执行完之后,终于进入构造器,执行构造器内的初始化语句;
控制台产生如下输出:
然后执行构造其中的剩下语句,将apple1中的各个字段的值打印出来:
其中的static3与value3没有指定初始值,因此默认为0;
会到main方法,终于完成了apple1对象的创建,开始执行apple2的创建:
控制台中的输出:
最后,终于执行到了main方法的第一条语句,实属不易:
通过上述例子,可以对java中的初始化过程做出一个总结:
构造器的构造过程涉及更复杂的知识,本菜鸡还没有学到,等后面学习了相应的知识再进行补充;
由于刚刚接触java,文章中错误不少,求看官大佬指正;