实例通俗理解Java中的多态
多态定义
不同类的对象(有共同的父类)面对同一消息(消息可对应理解为方法)做出不同的响应。看一个简单的例子:
定义父类:Peson.java,方法:eatdinner
public class Person {
public Person(){}
public void eatdinner(){
System.out.println("a person is eatting dinner");
}
}
定义子类:Student.java,方法:eatdinner,继承自Person并重写了父类方法
public class Student extends Person{
public Student(){}
@Override
public void eatdinner() {
System.out.println("a student is eatting dinner");
}
}
定义子类:teacher.java,方法:eatdinner,继承自Person并重写了父类方法
public class Teacher extends Person {
public Teacher(){}
@Override
public void eatdinner() {
System.out.println("a teacher is eatting dinner");
}
}
测试多态效果:test.java,注意看注释
public class test {
public static void main(String[] args){
Person Pstudent = new Student();//Java中的向上转型:父类(Person类)引用指向子类实例对象(Student对象)
Person Pteacher = new Teacher();//Java中的向上转型:父类(Person类)引用指向子类实例对象(Teacher对象)
Pstudent.eatdinner();
Pteacher.eatdinner();//不同对象面对同一消息(函数)做出的不同反应,注意看以下运行结果
}
}
不清楚对象之间的向上转型和向下转型的区别请戳这里,另附上基本数据类型的转型
运行结果:
a student is eatting dinner
a teacher is eatting dinner
可以看出不同对象面对同一消息(函数)做出的不同反应,注意看以上运行结果。
总结多态存在的三个必要条件
-
继承
Teacher和Student均继承了父类Person
-
重写
Teacher和Student均重写了Person类中的eatdineer
-
向上转型
父类引用指向子类实例对象
多态的作用
解耦合,消除类与类之间的关系,结合场景理解:比如面对一个类中有一些功能类似的方法,像实现一个人打球的方法,如果一个人会打乒乓球,篮球,羽毛球,如果不用多态,实现如下:
- 非多态版本:
既然是打球,首先得有个球吧,那定义三个球类吧,篮球乒乓球羽毛球三个类,球有什么方法,那只有被打了,并实现方法played
定义Basketball.java
public class Basketball {
public void played(){
System.out.println("打会儿篮球");
}
}
定义Badminton.java
//羽毛球
public class Badminton {
public void played(){
System.out.println("打会儿羽毛球");
}
}
定义Tabletennis
public class Tabletennis {
public void played(){
System.out.println("打会儿乒乓球");
}
}
打球得人来打吧,那再定义个person类
person.java
public class person {
//重载实现打三种球
public void playball(Basketball basketball){
basketball.played();
}
public void playball(Tabletennis tabletennis){
tabletennis.played();
}
public void playball(Badminton badminton) {
badminton.played();
}
}
实现打球playball功能,当然是你给我传什么球person打什么球,但是每个球的类型都不一样,那怎么办?所以得重载的方法实现三个打球的方法,分别传不同的球给人,刚好完美解决这个问题。写demo测试一下:
public class demotest {
public static void main(String[] args){
//实例化人、篮球、羽毛球、乒乓球
person p1 = new person();
Basketball basketball = new Basketball();
Badminton badminton = new Badminton();
Tabletennis tabletennis = new Tabletennis();
//球传给人,开始打球
p1.playball(basketball);
p1.playball(badminton);
p1.playball(tabletennis);
}
}
输出结果如下,可以看出实现了这三种功能:
打会儿篮球
打会儿羽毛球
打会儿乒乓球
那么问题来了,如果现在又学会了足球呢?除了实现一个足球的类之外,还得在person类里加一个打足球playball方法,person类修改如下:
public class person {
public void playball(Basketball basketball){
basketball.played();
}
public void playball(Tabletennis tabletennis){
tabletennis.played();
}
public void playball(Badminton badminton) {
badminton.played();
}
public void playball(Football football) {
football.played();
}
}
以后再添加一种球,就得修改一下person,十分麻烦,有没有一种方法不用修改person类,对于这种打球的功能,实现一个统一的方法,当你传入不同的球,能让person自己打不同的球。这不就是多态吗?不同类的对象(各种球)面对同一消息(打球的函数)做出不同的响应(实现打不同的球)。那来写一下吧。首先实现所有球的父类Ball.java
- 多态版本
public class Ball {
public void played(){
System.out.println("打会儿球吧");
}
}
然后分别实现篮球、羽毛球、乒乓球类,都继承了Ball类
public class Basketball extends Ball {
@Override
public void played(){
System.out.println("打会儿篮球");
}
}
public class Badminton extends Ball{
@Override
public void played(){
System.out.println("打会儿羽毛球");
}
}
public class Tabletennis extends Ball {
@Override
public void played(){
System.out.println("打会儿乒乓球");
}
}
实现person类:
public class person {
public void playball(Ball ball){
ball.played();
}
}
测试:
public class demotest1 {
public static void main(String[] args){
person p1 = new person();
Ball basketball = new Basketball();//父类(ball)引用指向子类实例(basketball)
Ball badminton = new Badminton();
Ball tabletennis = new Tabletennis();
p1.playball(basketball);
p1.playball(badminton);
p1.playball(tabletennis);
}
}
结果:
打会儿篮球
打会儿羽毛球
打会儿乒乓球
如果此刻再来了一个足球或者其他什么球,也没有关系,只要让其继承了Ball父类,然后重写了父类中的方法就可以作为参数传入到person类中的playball方法中,在实际操作中,Ball父类更多采用的接口而不是类,因为其中定义的方法并没有用到,都是调用子类中重写的方法。
多态注意点
三个条件:继承;重写;向上转型。
向上转型后,父类引用------>子类实例,方法和变量的该调用父类还是子类需要注意,和普通的继承关系不一样,具体总结如下:
类型 | 调用方式 |
---|---|
同名静态方法 | 调用父类的 |
同名成员方法 | 调用子类的 |
同名静态成员 | 调用父类的 |
同名成员成员 | 调用父类的 |
多态应用场景
个人理解,很多向上转型的场景其实都用到多态了,这里需要理解一下,首先向上转型,肯定是父类引用----->子类实例,这里其实已经有了上面提出的多态两个条件了,继承和向上转型,然后调用一个子类方法,很容易就满足了多态的条件。那么多态有哪些具体场景:
-
用于方法的参数中
上述的打球就是一个很好的例子
-
用于方法的返回类型中
接着以打球举个例子,上述的basketball、tabletennis等对象都是通过new出来的,对于这些有共同父类的对象,能不能用同一种方法来实现实例化对象,而不是直接new,在主程序中直接new,如果别人没有看过你的basketball、tabletennis等类的源码不会觉得这些球类之间有共同的父类的,个人理解,这写法都是为了方便理解。
//以前写法
public class demotest1 {
public static void main(String[] args){
person p1 = new person();
Ball basketball = new Basketball();
Ball badminton = new Badminton();
Ball tabletennis = new Tabletennis();
p1.playball(basketball);
p1.playball(badminton);
p1.playball(tabletennis);
}
}
//工厂类写法
public class BallFactoryTest {
public static void main(String[] args){
Ball ball1 = BallFactoryDemo.Ballproduce("basketball");
Ball ball2 = BallFactoryDemo.Ballproduce("badminton");
Ball ball3 = BallFactoryDemo.Ballproduce("tabletennis");
person p1 = new person();
p1.playball(ball1);
p1.playball(ball2);
p1.playball(ball3);
}
}
那么BallFactoryDemo是个什么呢,不用想的很复杂,其实就是个if-else嵌套写的,代码如下:
package BallFactory;
public class BallFactoryDemo {
//用Ball父类(方法返回类型为Ball)来接收子类实例对象(实际返回子类实例)----->多态
public static Ball Ballproduce(String ball){
if (ball.equals("basketball")){
return new Basketball();
}else if (ball.equals("badminton")){
return new Badminton();
}
else if (ball.equals("tabletennis")){
return new Tabletennis();
}
else{
System.out.println("no ball");
return null;
}
}
}
可以看出BallFactoryDemo类就像一个Ball工厂一样,可以生产出各种不同的Ball对象,其返回值可以是Ball的各种子类,这一点就很好的利用到了多态,使用父类引用接收了子类实例,而上面的BallFactoryDemo类实际上就是个工厂类,这就是传说中设计模式中的工厂模式,是不是不知不觉就get了工厂模式和多态知识点。