龙哥网

龙哥网

JAVA面向对象之多态(面向对象的多态)
2022-03-01

JAVA面向对象之多态

多态的形成

多态的形成条件大概有三个

#1、有继承

#2、有重写

#3、有父类引用指向子类对象

我们可以写下面一个例子

Animals.java

public class Animals { 
    public void run(){ 
        System.out.println("Animals is runing");
    }
}

Dog.java

public class Animals { 
    public void run(){ 
        System.out.println("Dog is runing");
    }
}

Cat.java

public class Animals { 
    public void run(){ 
        System.out.println("Cat is runing");
    }
}

Girl.java

public class Girl { 

    public void feed(Animals animals){ 
        animals.run();
    }

    public static void main(String[] args) { 
        Animals animals = new Animals();
        Animals dog = new Dog();
        Animals cat = new Cat();
        Girl girl = new Girl();
        girl.feed(animals);
        girl.feed(dog);
        girl.feed(cat);
    }
}

我们得到的结果是

Animals is runing
Dog is runing
Cat is runing

这个结果其实在我们的意料之中,为什么呢,因为我们传入feed的就是animalsdogcat,这其实就是一种多态,

但是我想说,我们定义的是一个Animals啊,这是不是无法解释的通了,不急我们往下看

静态类型和动态类型

我们来看一段小代码

Animal animal = Math.random() > 0.5 ? new Dog():new Cat(); 

我们知道animals指向的一只猫还是一条狗嘛?

实际上我们并不知道,指向的是谁只有程序运行起来我们才知道,但是我们知道的是一定指向animals的类或者他的子类

那我们就叫 Animal为 animal 的静态类型,或者叫编译类型或者叫申明类型或者叫外观类型。而等于右侧的我们叫动态类型,也叫运行时类型或者叫实际类型。

重载方法的调用

我们在调用一个虚方法的时候,jvm会在适当的时候帮我们选择合适的方法版本,有的时候在编译期、有时是在运行时,这个方法版本的选择过程我们可以称之为方法分派

当设计类时。方法的重写使得子类能够重写父类的方法。

当子类对象调用重写的方法时,调用的是子类的方法,而不是父类中被重写的方法。

Java虚方法你可以理解为java里所有被overriding的方法都是virtual的,所有重写的方法都是override的。

我们看一个例子

Human.java

public class Human { 
}

Man.java

public class Man extends Human{ 
}

Woman.java

public class Woman extends Human{ 
}

Party.java

public class Party { 
    
    public void play(Human human){ 
        System.out.println("Human is playing!");
    }

    public void play(Man man){ 
        System.out.println("Man is playing!!");
    }

    public void play(Woman woman){ 
        System.out.println("Woman is playing!!");
    }
    
    public static void main(String[] args) { 
        Party party = new Party();
        Human human = new Human();
        party.play(human);
        Human man = new Man();
        party.play(man);
        Human woman = new Woman();
        party.play(woman);
    }
}

得到的结果一定出乎你的意料

Human is playing!
Human is playing!
Human is playing!

事实上这是因为虚拟机在选择重载方式时,是通过静态类型决定的而不是动态类型。由于静态类型编译时就可知,事实上虚拟在编译期就已经知道选择哪一个重载方法,并且把这个方法的符号引用写在了invokevirtual的指令中。

在这个例子上面就是编译前我就知道我要调用的是第一个play方法

重写方法的调用

先在子类中找,子类中没有到父类中找

用多态的形成中的例子就是,先在dog中找run的方法,dog中没有的时候去animals中找,这个是符合实际思维的

重写方法的调用时依据运行时的类型决定的

重载和重写

重载只是选择了调用方法的版本。

重写是具体明确了调用谁的方法。

举一个略显变态的例子

Animal.java

public class Animal { 
    public void eat(){ 
        System.out.println("animal is eating!");
    }
    
    public void eat(String food){ 
        System.out.println("animal is eating "+food);
    }
}

dog.java

public class Dog extends Animal{ 
    @Override
    public void eat() { 
        System.out.println("dog is eating!");
    }
    
    @Override
    public void eat(String food) { 
        System.out.println("dog is eating " + food);
    }
    
    public static void main(String[] args) { 
        Animal animal = new Dog();
        animal.eat("meat");
    }
}

得到结果

dog is eating meat

其实不难理解java重载会选择Animal::eat(String food)这个方法,具体的调用谁的方法取决的动态类型,我们的动态类型是Dog就会调用狗的方法

免责声明
本站部分资源来源于互联网 如有侵权 请联系站长删除
龙哥网是优质的互联网科技创业资源_行业项目分享_网络知识引流变现方法的平台为广大网友提供学习互联网相关知识_内容变现的方法。