Java中类的加载机制---父类和子类的多态调用

一个最经典的程序是这样的:

public class ExtendsInstanceTest {

	private String baseName = "base";

	public ExtendsInstanceTest() {
		callName();
	}

	public void callName() {
		System.out.println(baseName);
	}

	static class Sub extends ExtendsInstanceTest {
		private String baseName = "sub";

		public void callName() {
			System.out.println(baseName);
		}
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ExtendsInstanceTest b = new Sub();
	}

}

他的输出结果是  null

1)

上面程序最大的难点,也是最重要的地方就是:在父类的构造函数中调用了虚函数,并且这个函数被子类重载了

2)

继承的时候,子类与父类有着同名的属性和同名的方法,关于同名的属性的初始化过程也是必须要了解的。同名的属性会不会被覆盖掉,同名的方法就是多态,同名的方法之间的调用是怎么样的。

3)

类构造的时候,Java机制是到底先给属性分配空间并赋值,还是先处理 构造函数,换句话说,当我们使用new操作符生成一个对象的实例的时候,类的加载机制是怎么样的,

如果这三个问题都搞定了,都理解了,上面的程序就很容易理解为什么输出是null了

Java机制里面有这样的一个原则就是:如果父类存在,子类可以不存在;如果子类存在,父类必须存在;

怎么理解上面的这句话呢,可以用实际的例子来说明,一个人结婚了但是没有小孩,对应着前半句的意思;如果他生了小孩,那么这个小孩子是一定有父亲的

到Java代码中这样看,如果我们实例化一个子类,必须先构造这个子类的父类,否则是错误的。也就是说,父类的存储空间的分配是在子类前面完成的;还可以这样说,当执行到子类的构造函数的时候,首先第一个代码是执行super(),哪怕你没有显示的写出来,他也是会去执行父类的实例化,这就是子类如果想完成初始化,必须先把父类搞定。

Java类加载的机制是第二个需要理解的地方就是:

1)类加载机制首先是  分配内存空间(堆空间,物理存储地址,每个属性都需要分配物理空间,【方法是不需要的】,且这个时候物理空间指向的是空null);

2)当空间分配好之后,进行属性初始化,把值放在栈空间中,前面的第一步过程中物理空间存储地址 指向  这个栈空间,这样就完成了属性值的初始化;

3)当属性值完成了初始化的时候,就开始调用构造函数了,执行构造函数里面的代码块

这个过程说白了,就是一个类加载的时候,执行过程,必须等所有的存储空间都分配好,才能够赋值,而不是一个属性分配好变量之后立刻就赋值,这个理解是错误的。

Java 中子类加载的机制是第三个需要理解的地方:

1)相关的类的加载机制还是跟  上面第二点相似,只是在子类初始化的时候必须先去初始化父类

2)只有 等Java机制给子类和所有的父类都分配了内存空间之后,先搞定堆内存,指向null;才会去  进行属性值的初始化,也就是在栈空间里面是属性的内容,前面分配的内存空间地址这个时候就指向  栈内存的  值;

3)最后就是注意  同名属性不会被子类给覆盖掉的,只是把父类的隐藏掉;同名方法是多态,只会去调用子类的重载方法,

这个规则说白了,就是当有父类和子类的时候,必须都所有的存储空间都分配好了,才能执行  属性的初始化,继而是构造函数;同时要明白一点,子类的构造函数是在父类的构造完成之后才会去执行,必须遵行只有了父亲才有孩子这个规则

上面的三个东西搞明白了,上面的程序就更好理解了

在main函数里面:new 了一个子类 Sub,Java机制是这样做的:

1)第一步首先在堆内存里面,为Sub分配内存空间,主要是属性 baseName ,地址变量指向null,

2)接下来执行Sub的构造函数,首先是执行super()函数,把父类搞出来,

3)进入父类的实例化,首先需要去在堆内存里面给父类分配内存空间,为父类的baseName分配地址,地址变量指向null;

4)由于父类不需要再也没有超类了,那么这个时候父类和子类的内存分配都做完了,接下来就是需要为  属性进行初始化的工作

5)首先是给父类的baseName执行初始化操作,在栈内存里面写上内容base,上面的为父类分配的地址变量  指向  这个栈内存

6)接下来是做父类的构造函数,完成父类的实例化,构造函数里面的代码是执行了一个虚函数,这个时候首先要看子类有没有重载这个函数,多态的调用

7)子类有重载,所以调用子类的方法,但是子类的baseName还没有初始化,所以就没有直接打出null了

8)父类创建完毕,接下来就是去执行子类的创建工作了,

9)首先为子类的属性进行初始化,在栈内存里面放上内容 sub;

10)接下来是去执行子类的构造函数,没有,是默认的无参

整个过程就完整了,

上面的例子最主要的就还是:关于在构造函数里面执行多态方法的时候,应该注意的地方

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券