Java继承与初始化全过程解析

霸气de小男生 提交于 2020-12-19 10:40:29

Java初始化简介

一般来说,在Java中,每个类产生的编译代码都存在于它自己的独立文件中,该文件只有在使用程序代码时才会被加载,也就是说,类的代码只有在初次使用时才加载。但是,如果存在static的话,就不一样了,当访问static字段或static方法时,也会发生加载。最常见的static方法是构造器方法了,虽然没有显示写明static关键字。所以,更准确地讲,Java的类应该是在其任何static成员被访问时加载的。

Java继承的概念

Java面向对象的三大特征是继承、封装、多态。继承的概念顾名思义,类似父子关系,子继承父亲。继承通常被认为是is-a关系,即子类是一个父累,可以这样理解。例如,大黄鸭是一个鸭子。虽然继承是面向对象的一个重大特性,但是对于项目的架构来说,继承应该慎用,常用的模式有组合,组合即一个类中持有另外一个类的对象,例如我们要构造一个汽车时,总要持有发动机、门、窗户、轮子等这些类的具体对象,显然汽车不是一个发动机,也不是一个轮子,它们的关系是has-a的关系,因此需要使用组合模式来设计。在OOP中,使用继承的一个优点是它可以向上转型(upcasting),即子类可以持有父类的方法,或者说子类是一个父类,通过向上转型,进而引出了多态的优点,即当有方法被覆盖时,父类类型的子类对象可以从子类依次向上查找,知道遇到该方法便可调用,这一优点为程序的可扩展性奠定了基础,如要新建一个子类,只用重新覆盖父类中已有的某个方法即可,在调用时即可实现多态的特性。然而,继承的使用场合应该尽量谨慎,使用前应该问问自己是否需要向上转型,如果后续业务中,必须要进行向上转型,那么就使用继承;否则,大可不必使用继承。

那么,在继承这种设计模式中,程序的加载过程是怎么进行的呢?父类的构造方法会不会加载?如果有static字段或方法,又是如何加载呢?

含有继承的程序加载过程

下面,我们以一个继承关系的两个类来探讨该问题,类中包含有static字段。

首先,写出基类的代码:


package demo0812.demo1;

public class SupClass {

		private int i=9;
		protected int j;
		public SupClass() {
			System.out.println("i="+i+",j="+j);
			j=39;
		}
		private static int x1=printInit("SupClass.x1 initialized");
		protected static int printInit(String str) {
			System.out.println(str);
			return 47;
		}
}
写出子类的代码:



package demo0812.demo1;

public class SubClass extends SupClass {

	private int k=printInit("SubClass.k initialized");
	public SubClass() {
		System.out.println("k="+k);
		System.out.println("j="+j);
	}
	
	private static int x2=printInit("SubClass.x2 initialized");
	public static void main(String[] args) {
		System.out.println("SubClass constructor");
		SubClass subClass = new SubClass();
	}
}
从上面的两个类代码可以看出,基类中含有一个static方法和一个static字段,另外还包含一个显示的构造方法。那么在程序执行过程中,当然先访问SubClass.main()方法,这是程序的入口,于是加载器开始启动并找出SubClass的编译代码(SubClass.class文件),然后,注意到该类有个关键字extends,说明它有个父类,于是,加载器便去加载父类的代码,即SupClass.class文件。加载器发现父类中含有一个静态字段,于是加载器先加载这个静态字段,同时也加载了静态方法,于是输出
SupClass.x1 initialized
,同时将x1的值赋为47。在加载完父类的静态区域以后,开始加载子类的静态区域,加载器于是看到,在子类中,也有一个静态字段x2,并且对它进行初始化,于是输出
SubClass.x2 initialized
,同时,将x2的值赋为47。这时,父类和子类的静态区都已经加载完成,于是,接着读取main函数,首先输出main中的第一行命令
SubClass constructor
接着,执行第二行命令,即对子类进行实例化,注意对子类实例化之前一定会对父类进行实例化,于是输出
i=9,j=0
接着,对子类进行按序执行,首先输出
SubClass.k initialized

并对k赋值为47,最后输出


k=47
j=39



这就是这段代码的总体的加载流程。





易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!