做梦都要背出来的三个必要条件

  1. 要有继承
  2. 要有重写
  3. 父类引用要指向子类对象

    三种实现方式

  4. 接口实现
  5. 继承父类进行方法重写
  6. 同一个类内进行方法重载(只有返回值类型不同不叫重载)

代码实例

package cn.haien.polymorphism;

public class Animal {
    public void voice() {
        System.out.println("普通动物的叫声");
    }
}

class Cat extends Animal{
    public void voice() {
        System.out.println("喵喵喵");
    }
    public void catchMouse() {
        System.out.println("抓老鼠");
    }
}

class Dog extends Animal{
    public void voice() {
        System.out.println("汪汪汪");
    }
}


package cn.haien.polymorphism;

public class Test {
    public static voidtestAnimalVoice(Animal c) {//形参使用多态
    //编译时,扫描到Animal
                                              //并发现其中有voice这个方法就通过了
        c.voice();
    if(c instanceof Cat)//instanceof 表示判断前者是不是后面类的实例化对象
        ((Cat)c).catchMouse();//如果是的话,强制转换为Cat类型并调用它自己的方法
    }

public static void main(String[] args) {
    Animal a = new Cat();
    Animal b = new Dog();
    testAnimalVoice(a);
    testAnimalVoice(b);
    Cat a2 = (Cat)a;//只能把引用该子类的父类对象强制转换为相应子类,其他都不行
    //Cat b2 = (Cat)b;
    a2.catchMouse();
    //b2.catchMouse();//编译通过,但运行出错,编译通过是因为编译器比较傻,你让它强制转换为什么它就强制转换为什么
                    //但是真正运行的时候实际是什么就是什么,不能把一只狗强制转换为一只猫
}

}
其实返回值也可以使用多态

内存分析

  1. 编译文件,从上到下、从左到右开始扫描,首先将Test加载到方法区即帧中(在Test文件中点运行)
  2. 扫描到main方法则开始运行
  3. 遇到类名则将其加载到帧中
  4. 遇到new则创建对象并在栈中分配内存,那么要先调用类的构造器。

    PS:构造器中实际隐式地传入this和supper,,而supper指向上级父类,一直找到Object类后开始一级一级往下执行构造器,每一级的this都指向最终要创建的对象而不会不同,只有supper是不同的

  5. 遇到testAnimalVoice方法开始传参,开辟栈帧存放形参c,同样指向堆中的类构造
内存情况
内存情况

内存深入分析

package cn.haien.polymorphism2;

public class HttpServlet {
    public void service() {
        System.out.println("HttpServlet.service()");
        doGet();//实际上是this.doGet();
    }
    public void doGet() {
        System.out.println("HttpServlet.doGet()");
    }
}

class myServlet extends HttpServlet{
    public void doGet() {
        System.out.println("myServlet.doGet()");
    }
}

package cn.haien.polymorphism2;

public class TestHttpServlet {
    public static void main(String[] args) {
        HttpServlet a = new myServlet();
        a.service();//内含doGet的调用,实际上调用的是子类myServlet的doGet函数
    }
}