多态机制原理解析--从内存角度分析

时间:2021-09-22 21:56:06

回头看多态,又有了新的认识。
理解多态主要搞清楚以下几个问题就可以了:(以父类Pet,子类Dog为例)
1.为什么可以用表达式 Pet p1= new Dog(); //为什么可以用父类的引用指向子类创建的对象?
2. 当用父类的引用p1,操作子类的对象的时候,为什么可以操作复写(override)的Method,怎么知道该Method是子类的而不是父类的?
3. 当用父类的引用p1操作子类的时候,有哪些限制?

下面进行解答:
理解多态的前提是理解继承(面向对象的三个基本特征:封装、继承、多态)
继承在此不做多的解释,只需要知道两点:1.父类中private 方法或者属性是不能够被继承到子类中;2. 子类实际又两部分组成,一部分是从父类那里copy过来的,还有一部分是自己的。从内存的角度来说,子类在初始化的时候,先初始化继承的那部分,也就是父类的东西(这里的初始化本质可以理解为复制,后面会通过实例进行详细解读),再初始化自己特有的部分。

例如下图,父类Pet中有属性a、还有三种不同类型的sayX方法。子类Dog继承了父类的属性a、public say()以及Protected say1(),还有自己的方法cute。

多态机制原理解析--从内存角度分析

下面说一下多态机制的原理

  1. 执行Pet p1=new Dog(); p1虽然是Pet类型的引用,但实际仍然指向Dog,只是操作范围只有从父类继承来的哪些属性或者方法。比如,本例中,p1实际操作范围,只有a,say,say1。不能操作say2,因为say2并没有继承到Dog类中。另外,子类在初始化的时候,也会将父类的方法申明所使用的偏移量赋值一份。
  2. JVM在执行class文件的时候,也就是在内存中执行Java的程序的时候,会将父类的Method,放到Method Table 里面,子类复写了父类中的方法后,初始化子类的时候,就会将Method Table中相应的Method进行覆盖。
    另一方面,所有派生类中继承于基类的方法在方法表中的偏移量跟该方法在基类方法表中的偏移量保持一致这也就是为什么可以准确的调用Method。
  3. 结合以上两点:p1.say() 之所以能够动态绑定实现多态,本质是Method Table进行了维护。之所以可以写成这样,Pet p1=new Dog() 是因为继承,is-a关系:子类是一种父类。

下面看demo:

class Pet {
    int a = 0;

    public void say() {
        System.out.println("Pet say");
    }

    protected void say2() {
        System.out.println("Pet say2");
    }

    private void say3() {
        System.out.println("Pet say3");
    }
}

class Dog extends Pet {
     public void cute(){ System.out.println("Dog cute"); }
     public void say(){ System.out.println("Dog say");  }
}

class Cat extends Pet {
     public void cute(){ System.out.println("Cat cute"); }
     public void say(){ System.out.println("Cat say");  }

}
public  class Zoo {

    public static void main(String[] args)
     {
       Pet p1 = new Dog();
       p1.say(); //首先解析一次,得到偏移量,调用方法
       p1.say2();//p1 可以调用Protected,不可以调用private类型
       Pet p0= new Pet();
       p0.a=2;//先初始化了子类,后修改父类的属性,通过打印,
       //可以知道,继承后的子类初始化本质是复制,即从父类拷贝了一份“类型+初始值”,然后父类数值上怎么变,和子类无关。
       System.out.println("p0.a=="+p0.a);
       System.out.println("p1.a=="+p1.a);
       }

       Pet p3=new Cat();
       p3.say();
}

打印结果:
Dog say
Pet say2
p0.a==2
p1.a==0
Cat say

补充:
1.Java不支持多继承,通过实现接口的方式来达到多继承的效果。
2. 实现接口的方式,不同于继承,所以不能保证相同的偏移量,也就意味着动态绑定会会比较繁琐,每次都要进行解析。

参考:
http://blog.sina.com.cn/s/blog_4de067e40100ma22.html
http://blog.csdn.net/gxnu/article/details/1801870

转载请注明出处:
http://blog.csdn.net/csp277