JAVA基础(十八)——多态

一、目录

  • 多态的介绍
  • 方法的多态
  • 对象的多态
  • 多态的细节
  • 动态绑定机制
  • 多态数组
  • 多态参数

二、多态的介绍

方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承的基础之上的。

多态分为方法的多态对象的多态,其中对象的多态是多态的核心。

三、方法的多态

方法的多态体现在重写和重载上。也就是说,重写和重载都属于多态,它们都是多态的一种表现形式。

package com.javaploy;

public class Functionploy {
    public static void main(String[] args) {
        B b = new B();
        b.sum(1,2,3);
        b.sum(1,2);

        A a = new A();
        a.say();
        b.say();
    }
}

class A{
    public void say(){
        System.out.println("Class A is be used");
    }
}

class B extends A{
    public int sum(int a, int b, int c){
        return a + b + c;
    }
    
    public int sum(int a, int b){
        return a + b;
    }

    public void say(){
        System.out.println("Class B is be used");
    }
}

比如上面这段代码中,sum方法和say方法,就都体现了多态。

四、对象的多态

重要的几句话:

  1. 一个对象的编译类型运行类型可以不一致。

    Animal animal = new Dog(); animal的编译类型是Animal,运行类型是Dog。

  2. 编译类型在定义对象时,就确定了,不能改变。但是运行类型是可以变化的。

    animal = new Cat(); 这时候,animal的编译类型还是Animal,但是运行类型已经变成了Cat。

  3. 编译类型看定义时,看=号的左边,运行类型看=号的右边。

例:

package com.javaploy.objectploy;

public class Animal {
    public void Cry(){
        System.out.println("Animal is Crying");
    }
}

class Dog extends Animal{

    public void Cry() {
        System.out.println("Dog is Crying");
    }
}

class Cat extends Animal{

    public void Cry() {
        System.out.println("Cat is Crying");
    }
}
package com.javaploy.objectploy;

public class PloyObject {
    public static void main(String[] args) {
        Animal animal = new Dog();
        animal.Cry();

        animal = new Cat();
        animal.Cry();
    }
}

输出结果为:

Dog is Crying
Cat is Crying

这里面,animal对象的编译类型一直是Animal,但是运行类型从Dog变成了Cat。

五、多态的细节

  • 多态的前提是:两个对象(类)存在继承关系

  • 多态的向上转型
    关于向上转型:

    1. 本质:父类的引用指向了子类的对象
    2. 语法:父类类型 引用名 = new 子类类型();
    3. 特点:编译类型看左边,运行类型看右边。
      可以调用父类中的所有成员(需遵守访问权限)
      不能调用子类中的特有成员(因为虽然运行类型是子类,但是能不能调用某个类成员,是在编译阶段,由编译器决定的,它的编译类型是父类,父类中没有子类特有的那个属性或方法,所以调不了。但如果调用子类中重写父类的方法,是可以调的,因为编译的时候,编译器找到了这个类,然后找的时候,还是会先从子类开始找。那么如果现在就想调用子类特有的成员,就需要向下转型。例如Cat cat = (Cat) animal;)。
  • 多态的向下转型

    1. 语法:子类类型 引用名 = (子类类型) 父类引用;
    2. 只能强转父类的引用,不能强转父类的对象。
    3. 要求父类的引用必须指向的是当前目标类型的对象。

      这句话怎么理解?
      比如:
      Animal animal = new Cat();
      这时候,父类的引用指向的对象是Cat。
      Cat cat = (Cat) animal; //正确
      Dog dog = (Dog) animal; //错误

    4. 当向下转型后,可以调用子类类型中所有的成员。
  • 调用属性,属性的值看编译类型。

  • instanceOf 比较操作符,用于判断对象的运行类型是否为XX类型或XX类型的子类型。

六、动态绑定机制

  • 动态绑定机制
    1. 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定。
    2. 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用。

看一段代码:

package com.javaDynamicBinging;

public class dynamicBanding {
    public static void main(String[] args) {
        A a = new B();
        System.out.println(a.sum());
        System.out.println(a.sum1());
    }
}


class A{
    public int i = 10;
    public int sum(){
        return getI() + 10;
    }

    public int sum1(){
        return i + 10;
    }

    public int getI(){
        return i;
    }
}

class B extends A{
    public int i = 20;
    public int sum(){
        return i + 20;
    }
    public int getI(){
        return i;
    }

    public int sum1(){
        return i + 10;
    }

}

上面这段代码,输出40和30。
如果把B类中的sum()方法去除,那么又会输出什么?

当B类中的sum()方法没有了之后,a.sum()自然而然的会去找其父类A的sum()方法。
结果发现里面又调用了getI()方法,这时候,由于B类和A类都有getI()方法,那么究竟调用哪一个?
这时候就用到了动态绑定机制,方法会和运行类型绑定,那么理所应当,调用B类中的getI()方法。
又因为属性不进行动态绑定,这时候方法在B类中,也就直接调用的B类的i的值。
所以输出30和30。

七、多态数组

多态数组:数组的定义类型为父类类型,里面保存的实际元素类型为子类类型。

比如有这样一道题目要求编写多态数组代码,创建一个Peroson父类,Student和Teacher子类。并将一个Person类对象和两个Student类对象以及两个Teacher类对象,一个五个元素同一放入数组中,并调用各自的say()方法。

package com.ployarr;

public class Javaployarr {
    public static void main(String[] args) {

        //这里创建了父类的数组,所有数组内可以放Person类,以及它的子类。
        Person[] persons = new Person[5];
        persons[0] = new Person("jack", 29);
        persons[1] = new Student("lilei", 12, 90);
        persons[2] = new Student("wang", 13, 92);
        persons[3] = new Teacher("Hong", 32, 10000);
        persons[4] = new Teacher("Bing", 30, 12000);

        //循环遍历多态数组
        for (int i = 0; i < persons.length; i++) {
            System.out.println(persons[i].say());
        }
    }
}

class Person{
    private String name;
    private int age;
    public String say(){
        return "My name is " + this.name + " My age is " + this.age;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

class Student extends Person{
    private double score;

    public Student(String name, int age, double score) {
        super(name, age);
        this.score = score;
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }

    public String say() {
        return super.say() + " My score is " + this.score;
    }
}

class Teacher extends Person{
    private double salary;

    public Teacher(String name, int age, double salary) {
        super(name, age);
        this.salary = salary;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public String say() {
        return super.say() + "My salary is " + this.salary;
    }
}


My name is jack My age is 29
My name is lilei My age is 12 My score is 90.0
My name is wang My age is 13 My score is 92.0
My name is Hong My age is 32My salary is 10000.0
My name is Bing My age is 30My salary is 12000.0

题目升级:
如果又加了一个条件,说是学生类和老师类中,分别添加一个特有的方法,那么如何在fori循环中调用?

这时候,可以在输出完say()方法后,再加一个判断条件,利用instanceof关键字。然后向下转型。

if(person[i] instanceof Student){
    ((Student)person[i]).study();    
};
if(person[i] instanceof Teacher){...};

八、多态参数

多态参数:方法定义的形参类型为父类类型,实参类型允许为子类类型。
这个比较简单,就不举列子了,感兴趣的同学可以自己尝试写一个例子出来。


版权声明:本文为qq_37466661原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。