基础篇-类加载执行顺序

类加载执行顺序:

public class ClassLoaderOrder {
	public static void main(String[] args) {
		new Child();
	}
}


class Base{
	public static String str = "父类静态全局属性";
	public String str1 = "父类普通全局属性";
	static{
		System.out.println("第一步:");
		System.out.println("父类静态代码块");
		System.out.println(str+"&&&&&&&&&&&&&&&&&&&&&&&&&");
	}
	{
		System.out.println("第二部");
		System.out.println("父类普通代码块");
		System.out.println(str+"$$$$$$$$$$$$$$$$$$$$$"+str1);
	}
	public Base(){
		System.out.println("第三部");
		System.out.println("父类构造方法");
	}
			
}

class Child extends Base{
	public static String str3 = "子类静态全局属性";
	public String str4 = "子类普通全局属性";
	static{
		System.out.println("子类静态代码块");
		System.out.println(str3+"&&&&&&&&&&&&&&&&&&&&&&&&&");
	}
	{
		System.out.println("子类普通代码块");
		System.out.println(str3+"$$$$$$$$$$$$$$$$$$$$$"+str4);
	}
	public Child(){
		System.out.println("子类构造方法");
	}
}

1、首先当我们从最简单的执行main()方法开始,当我们运行时,虚拟机会将类进行编译并加载到内存之中。

2、在创建对象时,系统会先查看该类是否有父类,如果有则执行顺序为:

父类静态成员(Object的最先)和父类静态代码块(它俩顺序按书写顺序) → 子类静态成员和静态代码块  →

父类非静态成员和代码块    →    子类非静态成员和代码块     →    父类构造方法     →   子类构造方法

上面打印结果:

第一步:
父类静态代码块
父类静态全局属性&&&&&&&&&&&&&&&&&&&&&&&&&
子类静态代码块
子类静态全局属性&&&&&&&&&&&&&&&&&&&&&&&&&
第二部
父类普通代码块
父类静态全局属性$$$$$$$$$$$$$$$$$$$$$父类普通全局属性
第三部
父类构造方法
子类普通代码块
子类静态全局属性$$$$$$$$$$$$$$$$$$$$$子类普通全局属性
子类构造方法

上面的结果显示先调用的父类构造方法再调用的子类代码块,这点有点和上面的顺序不同,查找原因中。

下面上一组挺有趣的java问题,下面代码不遵循java代码编写规则,仅仅为了构建这样题目:

public class ClassLoaderDemo {
	public static void main(String[] args) {
		new Son();
	}
}

//父类
class Father{
	private int i = 2;
	public Father(){
		System.out.println("构造方法"+i);
		display();
	}
	public void display(){
		System.out.println(i+"父类");
	}
	
}
//子类
class Son extends Father{
	private int i = 22;
	public Son(){
		System.out.println("构造方法子类");
		i = 222;
	}
	public void display(){
		System.out.println(i+"子类");
	}
}

感觉结果是多少呢?

答案为:0

刚开始想了很久,但是如果懂java的动态绑定就好理解多了。

程序执行顺序:

①:为父类的非静态成员变量分配内存空间,并初始化为默认值【默认值不是你指定的值,而是数值类型就是0,引用类型就是null,boolean就是false
②:为子类的非静态成员变量分配内存空间,并初始化为默认值【默认值不是你指定的值,而是数值类型就是0,引用类型就是null,boolean就是false````】
③:默认调用父类的无参构造器【如果用super()显示调用父类的其他构造器,则就不调用该无参构造器了】,注意:在执行构造器中的代码的时候,会先为父类的非静态成员变量赋值,此处赋的值就是你指定的那个值了,
如private int i = 2; 就把2赋值给变量了,并且还执行非静态代码块,至于先赋值还是先执行代码块,取决于你写的先后顺序;做完这些之后,才执行构造器中的代码;
④:执行子类构造器,在执行之前,也是和③一样,先处理本类中的非静态变量和代码块
所以,在第三部调用父类构造器的时候,会执行构造器中的
this.display();
方法,但是这个this到底指谁呢?是Base,还是Sun呢?是这样的,当用this调用变量时,就是编译时的类型,是Base【如果你在构造器中加一句输出this.i,打印出的就是Base的2】,当用
this调用方法时,就是当前正在引用对象的实例,此时由于,它动态绑定到子类(new的是子类),所以去输出子类的i,但是,此时还没有到达第④步,子类中的i只是被分配了内存,默认了0值,所以输出是0;

猜你喜欢

转载自blog.csdn.net/ysj4428/article/details/81391485
今日推荐