Java中类代码加载的顺序详解

一、无继承类创建对象的加载流程

为了验证加载流程,设计一个标准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)再从基类开始,直到子孙类,依次先进行实例变量初始化默认值,再进行实例变量初始化显示值,接着执行构造代码块,最后执行构造函数,再换下一个类。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值