一、无继承类创建对象的加载流程
为了验证加载流程,设计一个标准Java类,包含实例变量、类变量、静态代码块、构造代码块、实例方法、构造方法、静态方法,具体代码如下:
<span style="font-size:12px;">class Son
{
public String sonName = "sonName1"; //实例变量
public static int sonAge = 1; //静态变量
//静态代码块
static
{
System.out.println("in staticBlock:sonName can not be visited"+" sonAge="+sonAge);
System.out.println("this is Son's staticBlock");
sonAge = 2;
}
//构造代码块
{
System.out.println("in struBlock:sonName="+sonName+" sonAge="+sonAge);
System.out.println("this is Son's struBlock");
sonName = "sonName3";
sonAge = 3;
}
//构造函数
public Son(){
System.out.println("in struFunction:sonName="+sonName+" sonAge="+sonAge);
sonName = "sonName4";
sonAge = 4;
}
//实例函数
//实例函数不影响类加载流程,写出来是为了查看最终一些变量的值
public void sonShow(){
System.out.println("finally:"+sonName+" sonAge="+sonAge);
}
//静态函数
//静态函数没必要写,不影响类加载流程
//主函数
public static void main(String[] args){
Son son = new Son();
son.sonShow();
}
}</span>
该类运行在控制台运行结果如下:
根据结果,文字简单描述类对象加载过程:
(1)JVM(虚拟机)加载类 ———> 对类变量进行初始化 ——> 执行静态代码块代码
(2)进行实例变量初始化 ——> 执行构造代码块代码 ——> 执行构造函数代码
对这一加载过程,依据JVM的原理描述其底层实现过程:
首先,再讲解之前先提及一下Java代码运行时的内存结构,JVM为了方便内存管理,将内存分为若干区域,即栈区、堆去、方法区等,这里主要介绍前三种。
栈区:
(1)用于存储Java代码中所有的局部变量,当变量使用完,自动释放相应的内存空间。
堆区:
(1)存放数组和对象,即通过new创建的实例都存放在堆内存中;
(2)每个实例都会有一个内存地址,且实例中所有成员变量都会有默认的初始值;
(3)实例不在被使用时,将有Java回收机制自动回收
方法区:
(2)存放程序中函数代码以及类的静态变量
上面的代码在JVM中的实际运行流程,文字描述如下:
(1)JVM找到main函数,并在栈区开辟main函数区域
(2)执行到Son son时,JVM会在main函数区域创建Son类的引用,注意此时并没有加载Son类,即若是Son son;或Son son=null;代码并不会导致类的加载。
(3)执行到new Son();时,JVM开始加载Son类。首先在方法区开辟属于Son类的区域,并将类成员函数代码存入其中。接着
在Son类区域定义并初始化类变量,然后执行静态代码块。接着在堆区开辟一块son对象的内存,并初始化实例成员变量的默认值。然后初始化实例成员变量的显示值。接着执行构造代码块,最后执行构造函数代码。
(4)执行赋值运算符“=”,JVM将在堆区son对象的内存地址赋给栈区的son引用。
画图表示其流程如下:
小结:静态与类同步,实例与对象同步。
二、继承类创建对象的加载流程
这里以两层继承类为例,说明其创建对象的加载流程,代码如下:
<span style="font-size:14px;">class GrandPa
{
public String grandpaName="grandpaName1"; //实例变量
public static int grandpaAge = 1; //静态变量
//静态代码块
static
{
System.out.println("in staticBlock:grandpaName can not be visited"+" grandpaAge="+grandpaAge);
System.out.println("this is grandpa's staticBlock");
grandpaAge = 2;
}
//构造代码块
{
System.out.println("in struBlock:grandpaName="+grandpaName+" grandpaAge="+grandpaAge);
System.out.println("this is grandpa's struBlock");
grandpaName = "grandpaName3";
grandpaAge = 3;
}
//构造函数
public GrandPa(){
System.out.println("in struFunction:grandpaName="+grandpaName+" grandpaAge="+grandpaAge);
grandpaName = "grandpaName4";
grandpaAge = 4;
}
//实例函数
//实例函数不影响类加载流程,写出来是为了查看最终一些变量的值
public void grandpaShow(){
System.out.println("finally:"+grandpaName+" grandpaAge="+grandpaAge);
}
}
class Father extends GrandPa
{
public String fatherName="fatherName1"; //实例变量
public static int fatherAge = 1; //静态变量
//静态代码块
static
{
System.out.println("in staticBlock:fatherName can not be visited"+" fatherAge="+fatherAge);
System.out.println("this is father's staticBlock");
fatherAge = 2;
}
//构造代码块
{
System.out.println("in struBlock:fatherName="+fatherName+" fatherAge="+fatherAge);
System.out.println("this is father's struBlock");
fatherName = "fatherName3";
fatherAge = 3;
}
//构造函数
public Father(){
System.out.println("in struFunction:fatherName="+fatherName+" fatherAge="+fatherAge);
fatherName = "fatherName4";
fatherAge = 4;
}
//实例函数
//实例函数不影响类加载流程,写出来是为了查看最终一些变量的值
public void fatherShow(){
System.out.println("finally:"+fatherName+" fatherAge="+fatherAge);
}
}
class Son extends Father
{
public String sonName="sonName1"; //实例变量
public static int sonAge = 1; //静态变量
//静态代码块
static
{
System.out.println("in staticBlock:sonName can not be visited"+" sonAge="+sonAge);
System.out.println("this is Son's staticBlock");
sonAge = 2;
}
//构造代码块
{
System.out.println("in struBlock:sonName="+sonName+" sonAge="+sonAge);
System.out.println("this is Son's struBlock");
sonName = "sonName3";
sonAge = 3;
}
//构造函数
public Son(){
System.out.println("in struFunction:sonName="+sonName+" sonAge="+sonAge);
sonName = "sonName4";
sonAge = 4;
}
//实例函数
//实例函数不影响类加载流程,写出来是为了查看最终一些变量的值
public void sonShow(){
grandpaShow();
fatherShow();
System.out.println("finally:"+sonName+" sonAge="+sonAge);
}
//静态函数
//静态函数没必要写,不影响类加载流程
//主函数
public static void main(String[] args){
Son son = new Son();
son.sonShow();
}
}</span>
程序输出结果如下图:
加载过程总结如下:
(1)加载过程从基类开始依次加载;
(2)先从基类开始,直到子孙类,依次先进行每个类的类变量初始化和执行静态代码块,再换下一个类;
(3)再从基类开始,直到子孙类,依次先进行实例变量初始化默认值,再进行实例变量初始化显示值,接着执行构造代码块,最后执行构造函数,再换下一个类。