从内存的角度理解java中的继承

面向对象中的继承有很多现象,只从代码的角度很难去理解。如父子类的构造函数,方法的重载等,下面我们看一段代码:

package com.youren;

public class Test {
	public static void main(String[] args) {		
		
		Animal a = new Animal();
		System.out.println(a.i+"animal");
		a.disPlay();
		System.out.println("**************");
		
		Wolf w = new Wolf();
		System.out.println(w.i+"wolf");
		w.disPlay();
		System.out.println("**************");
		
		Animal aw = new Wolf();
		System.out.println(aw.i+"aw");
		aw.disPlay();
		System.out.println("**************");
		
		Animal a2w = w;
		System.out.println(a2w.i+"a2w");
		a2w.disPlay();
		
		
	}
}
class Animal{
    public int i = 2 ;
    public Animal(){
        this.disPlay();
    }

    public void disPlay() {
        System.out.println(i);
    }
}
class Wolf extends Animal{
    public int i = 22;
    public Wolf(){
        i=222;
    }
    public void disPlay(){
        System.out.println(i);
    }
}

下面我们来分析一下:

 wolf为animal的子类,他们都有一个 同名的成员变量和函数。

第一次打印:创建animal对象,如图:第一个2是animal实例化时,构造函数中调用方法打印的,此时成员变量i已经赋值了。

第二次打印:创建wolf对象,如图:第一个0,其实仍然是执行了父类animal的构造函数 。

第三次打印:创建了wolf对象,用animal的引用指向这个对象。如图:第一个0,依然调用父类animal的构造函数打印的,第二个2,为父类的成员变量初始化的值,第三个222,则是子类的方法覆盖了父类的 方法。

第四次打印:没有出现0或者2是因为构造函数已经执行过了 。

由此我们总结一下任意一个类的初始化过程:

第一步:初始化父类静态成员

第二步:父类静态代码块

第三步:子类静态成员

第四步:子类静态代码块

第五步:父类普通成员

第六步:父类代码块

第七步:父类构造器

第八步:子类普通成员

第九步:子类代码块

第十步:子类构造器


由此,我们分析第二次打印的执行过程:首先,初始化父类,此时父类 成员变量值 为2,此时父类构造函数中会执行一次 打印 成员变量值得操作,问题来了?此时父类构造器中执行的方法是:this.disPlay();这个this指的其实是子类对象,这个时候,正在执行父类 初始化操作,而子类的成员变量还没有开始初始化,子类的i值其实是为0的,故此时打印出来的值为0,而后面子类完成初始化后,调子类的方法应该不难理解了。

再来看看第三次打印的 执行过程,一样也是创建了子类对象,只是使用父类的引用指向子类对象。第一次打印的0依然是执行父类的构造器打印的 子类还没有来得及初始化的成员变量,第二次打印的2是怎么回事呢?为什么不是222?我们发现,父类的 引用 指向子类 的 对象时,这个引用访问的与子类同名的成员时,默认使用父类的成员。而访问重载的方法 时,执行的是子类的方法。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值