照例先上代码:
public class Fu { int num = 10; public void method(){ System.out.println("父类成员方法"); } }
public class Zi extends Fu{ int num = 20; @Override public void method(){ // 方法名一样,参数列表一样,继承,所以是覆盖重写 super.method(); System.out.println("子类成员方法"); } public void show(){ int num = 30; System.out.println(num); // 30 局部变量 System.out.println(this.num); // 20 本类成员变量 System.out.println(super.num); // 10 父类成员变量 } }
public class Demo05 { public static void main(String[] args) { Zi zi = new Zi(); zi.show(); zi.method(); } }
运行截图:
分析内存运行过程:
首先还是按惯常将三个.class信息放入方法区,但是注意因为有了继承关系,在子类的.class信息里有一个编译器自动生成的特殊标记[[super_class]],这个特殊标记是一个指向,指向了父类,告诉子类父类是谁。
然后要运行,那么Demo.class信息里的main方法就要进栈,然后顺序执行main方法里的内容,首先声明一个子类对象,右边赋值new出来的子类对象,则在堆里开辟一块空间存放这个子类对象的信息,并将这个子类对象在堆里的地址值赋值给栈里声明的该对象名。
注意,堆里存放的子类对象当中构建了一个完整的父类结构对象,(所以说,一定是先将父类构造完成了,子类才能继承它构建。)即里边是父外边是子。
我们先看那这个父类结构里有什么东西,有父类里声明的成员变量以及成员方法(注意,这个堆里的成员方法保存的都只是其在方法区的地址值)。
此时各个变量在哪块内存,我们分析清楚了,重点就看this关键字和super关键字。this指代的是调用该方法的对象,所以它应该指代的是子类对象,super指代被子类对象继承的父类对象(因此super关键字也不能用于static方法中,类永远先于对象构建完成)
new出来的子类对象的地址值,赋值给栈中声明的局部变量名,接着又调用一个子类的成员方法show(),根据对象名和.class信息在方法区找到该方法进栈运行(注意方法都要进栈运行),分别输出30,20,10,首先30根据就近原则输出局部变量值,而20调用this关键字,那么就指向其指代的子类内容,读取里面的20,进行输出。super关键字也是一样,指向指代的父类内容,读取里面的30,进行输出。
最后运行子类的method方法,优先找到方法区子类的同名方法进栈运行,但是此时注意!子类的这个方法中第一句super.method();运行了父类的同名方法,那么同样由super关键字指代的父类对象查找,父类的同名方法进栈。
然后从栈顶父类method运行,运行完就出栈,运行下一个。
完整内存演化过程图: