面向对象三大特性之——多态

mac2022-06-30  19

面向对象三大特性之——多态

一、引言

此篇文章来自一个初学Java不久的学生,内容的用词、深度、广度甚至部分理解不够到位,再加上Markdown语法的不熟练,所以排版不够美观。但还是希望有疑问的读者能够读完全文,大家遇到问题可以一起交流。谢谢!

二、初步理解多态

多态,顾名思义一种东西的多种形态。那面向对象里面为什么或者说哪里来的多态?要想顺理的理解多态,就需要你对封装和继承又很好的理解。内容可以参考我之前写的 面向对象三大特性之——封装 面向对象三大特性之——继承。 当然,也希望你读完这篇文章之后,能够完成下面几个问题。 1.什么是多态,为什么使用多态? 2.理解为什么会有抽象类和接口,二者的区别是什么? 3.理解使用多态类和几口实现多态的不同。

三、多态

一、基本概念

1.什么是多态?

答:同一个对象,在不同时刻表现出来的不同形态,在继承的子父类里面体现。

2.多态的前提?

答:

​ 1)要有继承或实现关系

​ 2)要有方法的重写

​ 3)要有父类引用指向子类对象

二、成员的访问特点

成员变量:编译看父类,运行看父类(编译看左边,运行也看左边)

成员方法:编译看父类,运行看子类(编译看左边,运行看右边)

People类(父类):

public class People { int age = 100; public void show() { int age = 200; System.out.println("年龄是:"+age); } }

Student类(子类):

public class Student extends People { int age = 10; @Override public void show() { int age = 20; System.out.println("年龄是:"+age); } public void study() { System.out.println("要好好学习"); } }

测试类:

public class Demo01 { public static void main(String[] args) { People p = new Student(); p.show(); } }

运行结果:

年龄是:20

总结:成员方法在编译的时候查看父类里面是否含有show()方法,如果有,在运行的时候在子类里面查找show()方法,然后执行子类方法的代码。由于子类继承了父类,所以只要父类能够查找到指定的方法,子类的方法里面就一定含有重写父类的方法。所以运行代码,产出结果。

三、多态的优点和缺点

优点:提高程序的扩展性。定义方法时候,使用父类型作为参数,在使用的时候,使用具体的子类型参与操作

缺点:不能使用子类的特有成员

四、多态的自动转型

为什么会出现自动转型?

举个例子,我想要打印子类的成员变量age。但是根据多态成员的访问特点来看。成员变量编译看左边,执行也看左边,根本没有出现右边(子类)的成员变量。于是多态的转型就出来了。

1.向上转型:也成为自动转型,即正常的运行方式

2.向下转型:称为强制转型。

主要就是这段代码的理解:

int age = ((Student) p).age; System.out.println(age);

p被声明为People类型,此时需要向下转型为People的子类Student类型。如果你能想到整型数据的强制转化,这里运用了相同的语法格式。(Student)p。那么此时的p则是Student类型的,然后再调用它里面的age对象,再赋值给main方法里面定义的变量age,打印输出。

四、抽象类

一、基本理解

当我们在做子类相同功能进行抽取时,有些方法在父类中并没有具体的体现,这个时候就需要抽象类了。在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类。口水话理解就是:抽象类就相当于一个没有劳动能力的父亲,但是心中却又有很多抱负(成员变量和常量),于是只能将自己的想法定义为抽象类,自己没有办法实现,之能把这些想法告诉自己的子类,让它们帮助自己去实现这些抱负(常量和方法)

二、抽象类的基本特点

1.抽象类和抽象方法必须使用 abstract 关键字修饰

//抽象类的定义 public abstract class 类名 {} //抽象方法的定义 public abstract void eat();

2.抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类

3.抽象类不能实例化,要想实现抽象类的方法,需要借助其子类实现,也就是多态的概念。

4.抽象类的子类可以是一下两种情况

​ 1)要么重写抽象类中的所有抽象方法

​ 2)要么是抽象类

三、抽象类的成员特点

成员变量:既可以是变量,也可以是常量

构造方法:空参构造,有参构造

成员方法:抽象方法,普通方法

五、接口

一、接口理解

接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用。Java中的接口更多的体现在对行为的抽象。这个在我们现实中的理解就好比,在离开继承的is…a关系之后,自然界还有一种关系就好比,你和老鹰会飞,飞机也会飞,你做梦的时候也会飞,但是你们三者很难找到一个继承的关系,所以接口就来了,它就类似于一个功能的开关。当给你连接这个接口的时候,你就可以获得这个功能。

二、接口的特点

接口用关键字interface修饰

public interface 接口名 {}

类实现接口用implements表示

public class 类名 implements 接口名 {}

接口不能实例化:这一点和抽象类的用法相同。要想实现接口的方法,需要借助其子类实现,也就是多态的概念。

接口的子类:要么重写接口中的所有抽象方法,要么子类也是抽象类

三、接口的成员特点

成员变量:只能是常量 ​ 默认修饰符:public static final

构造方法:没有,因为接口主要是扩展功能的,而没有具体存在

成员方法:只能是抽象方法

默认修饰符:public abstract

六、例子

一、抽象类与多态的结合

需求:定义一个抽象的Father类,定义一个Son类用来充当Father的子类,并且实现Father的抽象方法。在测试类里面,利用多态的方式,调用Son里面指定的方法

定义一个Father类:

public abstract class Father { public abstract void hobby(); }

定义一个Son类:

public class Son extends Father { @Override public void hobby() { System.out.println("我的爱好是打篮球"); } }

定义一个测试类:

public class Demo03 { public static void main(String[] args) { Father f = new Son(); f.hobby(); } }

代码执行结果:

我的爱好是打篮球

二、接口类与多态的结合

需求: 定义一个Fly的接口,里面包含两个不同定义方式的eat和fly方法。再定义三个方法People、Plane和Eagles用于实现接口。定义一个测试类,用前面提到的不同的方式去调用不同的方法和常量。

定义一个Fly的接口类:

public interface Fly { //省略了默认的修饰符的方式定义方法 void fly(); //加上默认的修饰符的方式定义方法,除方法名不一样外,两者的效果一样 public abstract void eat(); }

定义三个Fly接口的实现类,People、Plane和Eagles:

People类:

public class People02 implements Fly{ int num =1; //在子类中对接口中的方法进行重写 @Override public void eat() { System.out.println("我是人,我要吃饭"); } //在子类中对接口中的方法进行重写 @Override public void fly() { System.out.println("我是人,我做梦会飞"); } }

Plane类:

public class Plane implements Fly { int num = 2; @Override public void eat() { System.out.println("我是飞机,我不需要吃饭"); } @Override public void fly() { System.out.println("我是飞机,我可以飞的很高"); } }

Eagles类:

public class Eagles implements Fly { int num =3; @Override public void eat() { System.out.println("我是老鹰,我要吃蛇"); } @Override public void fly() { System.out.println("我是老鹰,我会飞翔"); } }

定义一个测试类:

public class Demo02 { public static void main(String[] args) { //利用多态的方式创建一个对象 Fly f = new People02(); f.eat(); f.fly(); //向下转型的运用 System.out.println(((People02) f).num); //正常的实例化Plane Plane p = new Plane(); p.eat(); p.fly(); System.out.println(()); Fly e = new Eagles(); //使用的是向下转型的方式调用这个方法,但是成员方法的访问特点是:编译看左边,执行看右边,所以使用下面的方式调用也可以 ((Eagles)e).eat(); e.fly(); //利用向下转型(强制转型)的方式,打印输出其实现子类的成员变量 System.out.println(((Eagles) e).num); } }

代码执行结果:

我是人,我要吃饭 我是人,我做梦会飞 1 我是飞机,我不需要吃饭 我是飞机,我可以飞的很高 2 我是老鹰,我要吃蛇 我是老鹰,我会飞翔 3
最新回复(0)