子父类在内存中的表现形式
来源:互联网 发布:115网盘会员淘宝 编辑:程序博客网 时间:2024/05/16 14:14
------- android培训、java培训、期待与您交流! -------
子父类在内存中的表现形式
当创建任何java对象时,程序总会先调用每个父类非静态初始化块、父类的构造器(总是从Object开始)执行初始化,最后才调用本类的非静态初始化块、构造器执行初始化。
关于调用父类的哪个构造器执行初始化问题:
1.子类构造器执行体的第一行代码使用super显示调用父类构造器,系统将根据super调用里传入的实参列表来确定调用父类哪个构造器
2.子类构造器执行体的第一行代码使用this显示调用本类中重载的构造器,系统将根据this调用里传入的实参列表确定本类的另一个构造器
3.子类构造器中既没有super调用,也没有this调用,系统将会在执行子类构造器之前,隐式调用父类无参数的构造器。
关于静态初始化块:静态初始化块是在类被jvm第一次加载时调用的,只调用一次,并且先调用父类的静态初始化块,然后是子类的,当所有静态初始化块被调用完后,才执行非静态初始化块和构造函数。
注意【super调用用于调用父类的构造器,this调用构造函数用于调用本类中另一个重载的构造器。super调用和this调用构造器都只能在构造器中使用,而且都必须作为构造器的第一行代码,因此构造器中的super调用和this调用最多只能使用其中之一,而且this调用构造函数在一个类中最多只能使用一次(所以不会产生循环调用构造器的现象)。】
继承成员变量和成员方法的区别:
public class TestDemo1 {public static void main(String[] args) {Animal animal=new Animal();System.out.println(animal.a);animal.run();animal.eat();Cat cat=new Cat();System.out.println(cat.a);cat.run();cat.eat();Animal aCat=new Cat();System.out.println(aCat.a);aCat.run();aCat.eat();}}class Animal{int a=1;public void run(){System.out.println("this is Animal run");}public static void eat(){System.out.println("Animal eat");}}class Cat extends Animal{int a = 2;public void run(){System.out.println("this is cat run");}public static void eat(){System.out.println("cat eat");}}/*运行结果: 1this is Animal runAnimal eat2this is cat runcat eat1this is cat runAnimal eat */
如果子类重写了父类中的方法,就意味着子类里面定义的方法彻底覆盖了父类中的同名方法,系统将不会将父类中的方法转移到子类中,对于实例变量则不存在这样的现象,即使子类中定义了与父类中同名的实例变量,这个变量依然不可能覆盖父类中定义的实例变量。
子类不会完全覆盖父类变量,它只是隐藏了父类中的实例变量。如果子类里面定义了与父类中已有变量同名变量,那么子类中定义的变量会隐藏父类中定义的变量。注意:不是完全覆盖,因此系统为创建子类对象时,依然会为父类定义的、被隐藏的变量分配内存空间。可以用super调用。
当变量的编译时类型和运行时类型不同时,通过该变量访问它引用的对象的实例变量时,该实例变量的值由声明该变量的类型决定。当通过该变量调用它引用的对象的实例方法时,该方法行为将由它实际所引用的对象来决定。
Animal aCat=new Cat()
aCat.a -->Animal中的a
aCat.run() -->Cat中的run方法
aCat.eat() -->Animal中的eat方法
总而言之:
成员变量:编译和运行都参考等号左边
成员函数:编译看左边,运行看右边
静态函数:编译和运行都看左边
访问子类对象的实例变量
子类的方法可以访问父类的实例变量,但是父类的方法不能访问子类的实例变量,因为父类根本无从知道它将被哪个子类继承,它的子类将会增加怎样的成员变量。
但是,在极端的情况下,可能出现父类访问子类变量的情况。例如:
class Base {private int i = 2;public Base() {this.display();}public void display() {System.out.println("this is fu display function..."+i);}}class Derived extends Base {private int i = 22;public Derived() {i = 222;}public void display() {System.out.println("this is zi display function.."+i);}}public class Test {public static void main(String[] args) {new Derived();}}/**运行结果: * this is zi display function..0 */
运行结果,可能和我们希望的不一样,首先澄清一个概念:java对象是由构造器创建的吗?实际情况是:构造器只负责对java对象实例变量执行初始化(也就是赋初始值),在执行构造器代码之前,该对象所占的内存已经被分配下来。这些内存里的值都是默认值。
上例中,this代表谁,当this在构造器中时,this代表正在初始化的java对象。此时的情况是:从源代码来看,此时的this位于Base()构造器内,但这些代码实际放在Derived()构造器内执行——是Derived()构造器隐式调用了Base()构造器的代码。由此可见,此时的this应该是Derived对象,而不是Base对象。
有一个问题,修改Base构造函数如下
public Base(){
this.display();
System.out.println(this.i);
System.out.println(this.getClass());
}
此时再执行程序,会发现输出:
/**运行结果:
* this is zi display function..0
*2
*class Derived
*/
为什么直接this.i时会输出2呢?
因为:这个this虽然代表Derived对象,但它却位于Base构造器中,它的编译时类型是Base,而实际引用一个Derived对象。通过输出this.getClass()
所以说:应避免在构造器中调用被子类重写过的方法。
构造器只负责对java对象实例变量执行初始化,在执行构造器代码之前,该对象所占的内存已经被分配下来,这些内存里的值是默认值。
- 子父类在内存中的表现形式
- JAVA的数据在内存中的表现形式
- 关于负数在内存表现形式
- 进程在内存中的影像
- 类在内存中的结构
- float在内存中的方式
- 负数在内存中的存储
- 对象在内存中的状态
- 程序在内存中的分布
- 程序在内存中的分布
- 数据在内存中的存储
- 程序在内存中的分布
- 程序在内存中的分布
- 程序在内存中的存放
- 程序在内存中的分布
- //String 在内存中的情况
- 程序在内存中的分布
- bmp在内存中的存放
- OpenGL ES学习笔记
- Linux下root用户使用Chrome
- gvim常用配置
- 按条件输出行
- linux学习入门4——linux文件系統基本结构(linuxcast.net)(倒转树状结构、命名机制、查看文件类型)
- 子父类在内存中的表现形式
- mempodroid 原理分析【转】
- webservice:使用urlConnection操作webservice
- set基本用法
- 15 Linux lsof Command Examples (Identify Open Files)
- RT5350调试总结
- Linux下使用wget下载jdk
- pygame开发的"雷电"游戏雏形
- http使用apache工具类提交数据