子父类在内存中的表现形式

来源:互联网 发布: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对象实例变量执行初始化,在执行构造器代码之前,该对象所占的内存已经被分配下来,这些内存里的值是默认值。





原创粉丝点击