基本介绍

多态:多【多种】态【状态】。

方法或对象具有多种形态,面向对象的第三大特征。

多态是建立在封装和继承基础之上的。

具体体现

方法的多态

方法的重写和重载都可以体现出多态

示例:

public class PolyMethod {
    public static void main(String[] args) {
        //方法重载体现多态
        A a = new A();
        //通过不同的参数个数去调用sum方法,就会去调用不同的方法
        //对sum方法来说,就是多态的体现。
        System.out.println(a.sum(10,20)); //调用两个参数的sum方法
        System.out.println(a.sum(10,20,30)); //调用三个参数的sum方法

        //方法重写体现多态
        B b = new B();
        a.say();//调用A类里面的say方法
        b.say();//调用B类里面的say方法
    }
}

class B{//父类
    public void say(){
        System.out.println("B say() 方法被调用...");
    }
}

class A extends B{//子类
    public int sum(int n1,int n2){ //和下面sum构成重载
        return n1 + n2;
    }

    public int sum(int n1,int n2, int n3){
        return n1 + n2 + n3;
    }

    public void say(){
        System.out.println("A say() 方法被调用...");
    }
}

结果:

30
60
A say() 方法被调用...
B say() 方法被调用...

对象的多态【核心】

  • 一个对象的编译类型和运行类型可以不一致
  • 编译类型在定义对象时,就确定了,不能改变
  • 运行类型是可以变化的
  • 编译类型看定义时=号左边,运行类型看=号右边
Animal animal = new Dog();#animal编译类型是Animal,运行类型是Dog
animal = new Cat();#animal运行类型变成了Cat,编译类型仍然是Animal

即父类的引用可以指向子类的对象。

示例:

public class Animal {
    public void cry(){
        System.out.println("Animal cry()");
    }
}
public class Cat extends Animal {
    public void cry(){
        System.out.println("Cat cry() 小猫");
    }
}
public class Dog extends Animal{
    public void cry(){
        System.out.println("Dog cry() 小狗");
    }
}
public class PoluObject {
    public static void main(String[] args) {
        //体验对象多态的特点
        //animal 编译类型是Animal 运行类型是Dog
        Animal animal = new Dog();
        //运行时,执行到下一行,animal的运行类型是Dog,所以cry就是Dog类中的cry
        animal.cry();
        //这时,animal的编译类型是Animal,运行类型变成了Cat
        animal = new Cat();
        //运行到下一行时,animal的运行类型是Cat,所以cry是Cat类中的cry
        animal.cry();

    }
}

Cat和Dog类继承了Animal类。

结果:

Dog cry() 小狗
Cat cry() 小猫

主人喂食物问题

题目:主人需要每天给猫喂鱼,给狗喂骨头,那么我们用java代码怎么体现主人的动作?

没有使用多态:

一个动物类,Dog和Cat都继承了动物类

public class Animal {
    private String name;

    public Animal(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

public class Cat extends Animal {
    public Cat(String name) {
        super(name);
    }
}

public class Dog extends Animal {
    public Dog(String name) {
        super(name);
    }
}

一个食物类,Fish和Bone都继承了Food类。

public class Food {
    private String name;

    public Food(String name) { //这个构造器覆盖了默认的无参构造器
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

public class Fish extends Food {
    public Fish(String name) {
        super(name);
    }
}

public class Bone extends Food{
    public Bone(String name) {
        super(name);
    }
}
public class Master {
    private String name;

    public Master(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

    //主人给小狗 喂食 骨头
    public void feed(Dog dog, Bone bone){
        System.out.println("主人" + name + "给" + dog.getName() + "吃" + bone.getName());
    }

    //主人给小猫 喂 鱼,方法重载
    public void feed(Cat cat,Fish fish){
        System.out.println("主人" + name + "给" + cat.getName() + "吃" + fish.getName());
    }
}

可以看到,在没有使用多态的时候,主人给狗喂骨头和主人给猫喂鱼的方法都得写出来,那么试想下,如果主人给动物喂食的东西逐渐变多的时候,我们需要重载的feed方法也就越来越多,这样对整个代码的复用性和可维护性都不太友好。

public class Poly01 {
    public static void main(String[] args) {
        Master tom = new Master("tom");
        Dog bigye = new Dog("bigye");
        Bone bone = new Bone("bone");

        tom.feed(bigye,bone);
    
        Cat cat = new Cat("cat");
        Fish fish = new Fish("fish");
        tom.feed(cat,fish);
    }

}

结果;

主人tom给bigye吃bone
主人tom给cat吃fish

使用多态:

public class Master {
    private String name;

    public Master(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    //使用多态机制,统一管理主人喂食的问题
    //animal编译类型是Animal,可以指向Animal子类的对象
    //food编译类型是Food,可以指向Food子类的对象
    public void feed(Animal animal, Food food)
    {
        System.out.println("主人" + name + "给" + animal.getName() + "吃" + food.getName());
    }
//    //主人给小狗 喂食 骨头
//    public void feed(Dog dog, Bone bone){
//        System.out.println("主人" + name + "给" + dog.getName() + "吃" + bone.getName());
//    }
//
//    //主人给小猫 喂 鱼,方法重载
//    public void feed(Cat cat,Fish fish){
//        System.out.println("主人" + name + "给" + cat.getName() + "吃" + fish.getName());
//    }
}

使用多态后,我们用animal指向Animal子类的对象,用food指向Food子类的对象,就可以很轻松的将方法重载多的问题解决。

结果:

主人tom给bigye吃bone
主人tom给cat吃fish

可以看出结果完全一致,但是使用多态后,就可以提高代码的复用性和可维护性。

多态的注意事项和细节

前提:两个对象或类存在继承关系。

多态向上转型

父类的引用指向子类的对象。【本质】

语法:父类类型 引用名 = new 子类类型();

特点:编译类型看=左边,运行类型看=右边

可以调用父类中的所有成员

不能调用子类中特定成员

最终的运行结果看子类(运行类型)的具体实现,即调用方法时,按照从子类(运行类型)开始查找方法,然后调用。

Animal animal = new Cat();

animal.eat();
animal.run();//当前运行类型没有时,才会去查找父类的,若当前运行类型有的话,则直接执行当前运行类型的。
animal.show();

多态的向下转型

语法:子类类型 引用名 = (子类类型) 父类引用;

  • 只能强制转换父类的引用,不能强制转换父类的对象
  • 要求父类的引用必须指向的是当前目标类型的对象
  • 当向下转型后,可以调用子类类型中所有的成员
public class Cat extends Animal {
    public void cry(){
        System.out.println("Cat cry() 小猫");
    }

    public void catchMoush(){
        System.out.println("catchMoush");
    }
}
public class Animal {
    public void cry(){
        System.out.println("Animal cry()");
    }
}
public class PoluObject {
    public static void main(String[] args) {
        //体验对象多态的特点
        //animal 编译类型是Animal 运行类型是Cat
        Animal animal = new Cat();
        
        //希望可以调用Cat中的catchMoush方法
        //多态向下转型
        //can not resolve mothed catchMoush()
        //animal.catchMoush();

        //编译类型是Cat,运行类型是Cat
        Cat cat = (Cat) animal;
        cat.catchMoush();
        
        

    }
}

从上面的图可以看出,要求父类的引用必须指向的是当前目标类型的对象,也就是原来的 animal就是指向Cat对象的,如果animal之前指向Cat对象,再强制转换成Dog,就会报错,如下:

java两种多态性 java多态的两种形式_多态

public class PoluObject {
    public static void main(String[] args) {
        //体验对象多态的特点
        //animal 编译类型是Animal 运行类型是Dog
        Animal animal = new Dog();
        Cat cat = (Cat) animal;
        cat.catchMoush();
 }
}

结果:

Exception in thread "main" java.lang.ClassCastException: com.company.poly_.objectpoly_.Dog cannot be cast to com.company.poly_.objectpoly_.Cat
	at com.company.poly_.objectpoly_.PoluObject.main(PoluObject.java:8)

属性没有重写之说,属性的值看编译类型


public class PolyDetail02 {


public static void main(String[] args) {
        //属性值看编译类型
        Base base = new Sub();
        System.out.println(base.count);
        Sub sub = new Sub();
        System.out.println(sub.count);
    }
}

class Base{
    int count = 10;
}

class Sub extends Base{
    int count = 20;
}

结果:

10
20

instanceOf 比较操作符

用于判断对象的类型(运行类型)是否是XX类型或者XX类型的子类型。

问题:是判断编译类型还是运行类型?运行类型

public class PolyDetail03 {
    public static void main(String[] args) {
        BB bb = new BB();
        System.out.println(bb instanceof BB);
        System.out.println(bb instanceof AA);
        //aa 编译类型 AA,运行类型 BB
        AA aa = new BB();
        System.out.println(aa instanceof AA);
        System.out.println(aa instanceof BB);

        Object o = new Object();
        System.out.println(o instanceof AA);
        System.out.println(o instanceof BB);

    }
}

class AA{} //父类
class BB extends AA{} //子类

结果:

true
true
true
true
false
false