Java中子父类的类在内存中的创建分析..经典的案例..

以上代码 如果在Main方法中 通过 Animal a = new Cat(); 来实现一个父类引用子类对象。

这句话首先是创建了一个Animal类型的a的引用,然后 new Cat();创建了一个Cat的对象,最后把这个a这个引用指向了
              new Cat();这个对象的地址。在这个对象创建的过程中其实有很多步骤
             
              首次访问:(在此没有显示的写出类中的构造方法)
                   顺序:子类的静态字段==》子类静态构造==》子类非静态字段==》父类的静态字段==》父类的静态构造==》父类的非静态字段
                             ==》父类的构造函数==》子类的构造函数
                         
              非首次访问:顺序是一样的,只不过少了中间静态字段和构造的过程
              
             这个过程依次类推直到递归到Object结束(在次过程中也是依次给父类分配内存的过程),且字段的在内存中的存储顺序是由上到下排列,object类的字段  排在最前面,
              原因是如果父类和子类出现了同名字段,则在子类对象创建时,编译器会自动认为这是两个不同的字段而加以区别。 
              
              说了对象的创建,其次是方法列表的创建:
              方法列表的创建是在编译时创建的,而对象的创建是在运行时,对象的创建是为了给方法列表一个引用的指针,使其它们动态关联起来。
              方法列表的创建顺序跟字段的的顺序是一样的,也是先父类后子类。(override 和 new 的不同 new主要是会阻断继承树,和隐藏父类方法,创建子类和父类同名的方法
              父先子后的原因是:在编译时创建方法列表的过程是,先生成父类的方法列表,而后在生成子类的方法列表的时候,会把父类的方法复制一份
              出来,然后拿子类的方法去和父类的比较,如果发现同名的方法,则看子类的方法修饰符是override 还是 new,如果是override 则覆盖父类
              同名的方法(以上所说的父类方法皆是virtual方法,并且这里说的覆盖只是说覆盖方法的实现,而并没有覆盖父类的方法列表,通过base.父类方法名还是可以调用父类的方法),
              如果是new  则在内存中的不同位置创建一个同名的方法。 不同名的,则直接创建。

     完成之后,我们可以通过 a这个引用来来调用Cat中的方法。

1.思考:如果把上例中Animal的play方法移到Cat中,在Main方法中打算通过a.Paly();来调用子类的Paly方法会发生什么现象? 会编译不通过,为什么呢?

按理说子类就是用来扩展父类的,理论上也允许子类有自己的特性啊(方法、字段……)。但问题不是出在子类,而是出在了调用的位置,不能通过a.Play();来调用这个方法,可能大家又不解了,会想 a就是通过 new Cat();这个对象啊出来的,为什么不能调用自己的方法勒,一层层的,最终我们找到原因是在 Animal a 这个申明引用的位置。

在此要引入OO 的一个原则: 关注对象原则——调用子类还是父类的方法,取决于创建的对象是子类对象还是父类对象,而不是它的引用类型,而引用指针类型不同的区别决定了不同的对象在方法表中不同的访问权限。

由此结合:子类可以调用父类方法和字段,而父类不能调用子类方法和字段  这个概念。就可以知道原因了。

在说说OO中的另一个原则:就近原则——对于同名字段或者方法,编译器是按照其顺序查找来引用的,也就是首先访问离它创建最近的字段或者方法。先贴一段代码,然后通过代码来分析。

View Code

在代码的Main方法中  Animal a = new Cat();  这个A是Animal 类型的,结合文章开始将的 在编译和运行过程中 子类、父类 的字段和方法以及实例化时候在内存中分配的先后位置可以得出:Animal 类中的 AnimalName 在内存中的位置一定位于 Cat中AnimalName在内存中的位置的前面,根据就近原则打印出的应该是Animal。

以上文章大致概括了在继承过程中的 在编译和运行过程中 子类、父类 的字段和方法以及实例化时候在内存中分配 和 执行的先后,以及两个原则,如有错误或者不足的地方请拍砖。后续后更深入的学习oo中的其它内容。

### 回答1: 这是一个Java子类对象的创建过程,具体步骤如下: 1. 在子类的构造方法,首先会调用父类的构造方法,完成父类对象的初始化。 2. 之后,子类成员变量和方法会被初始化和加载。 3. 如果有父类子类方法重名的情况,则子类的方法会覆盖父类的方法,成为实际调用时的方法。 4. 最后,子类对象被创建并返回给调用者。 ### 回答2: Java创建子类对象的过程可以分为以下几个步骤: 1. 加载父类子类的Class对象: 在Java程序,在使用之前需要先加载它们的Class对象,也就是的元数据。在创建子类对象的过程,首先需要加载父类子类的Class对象,这样才能够创建对象并初始化。 2. 分配内存: 在Java,对象都是在堆内存创建的。创建子类对象时,需要先在堆内存为它分配足够的内存空间。 3. 初始化对象: Java对象的初始化是通过执行构造函数来实现的。在创建子类对象时,首先要调用父类的构造函数进行父类对象的初始化,然后再执行子类的构造函数进行子类对象的初始化。 4. 初始化成员变量: 在初始化对象时,还需要初始化对象的成员变量。对于子类对象来说,它会先初始化父类的成员变量,然后再初始化子类的成员变量。 5. 返回对象: 创建子类对象的最后一步是将对象的引用返回给调用者,这样就可以使用这个对象了。 总结起来,创建子类对象的过程就是先加载父类子类的Class对象,然后在堆内存为对象分配空间,执行父类构造函数进行父类初始化,执行子类构造函数进行子类初始化,并最后返回对象引用。 ### 回答3: Java中子对象的创建过程包含了以下详细步骤: 1. 加载父类子类文件:在首次创建子类对象时,Java虚拟机需要将父类子类的Class对象加载到内存。 2. 分配内存空间:接着,Java虚拟机将会为子类对象分配内存空间,包括子类本身的实例变量以及从父类继承的实例变量。 3. 初始化默认实例变量:初始化子类对象的实例变量,包括从父类继承的和子类自己定义的实例变量,填充默认值。 4. 调用父类构造函数:子类对象在创建时需要先调用父类构造函数来初始化父类的实例变量。子类对象通过super关键字调用父类的构造函数。 5. 执行子类构造函数:父类构造函数执行完毕后,会执行子类的构造函数来初始化子类特有的实例变量。 6. 返回子类对象引用:当子类对象初始化完成后,Java虚拟机会返回该对象的引用,供程序员使用。 总的来说,Java中子对象的创建过程需要完成实例变量的内存空间的分配,初始化默认实例变量,调用父类构造函数和执行子类构造函数等多个步骤。理解Java中子对象的创建过程对于编写面向对象的程序和调试代码都非常重要。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wscwsc58888

只为正式环境创作

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值