文章目录
前言
面向对象编程有三大特性:封装、继承、多态。
封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据。对外界而已它的内部细节是隐藏的,暴露给外界的只是它的访问方法。
继承是为了重用父类代码。两个类若存在IS-A的关系就可以使用继承。,同时继承也为实现多态做了铺垫。
那么什么是多态呢?多态的实现机制又是什么?
提示:以下是本篇文章正文内容,下面案例可供参考
一、多态是什么?
所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。
简单来说,多态是同一个行为具有多个不同表现形式或形态的能力。
二:多态的好处
使用多态,我们可以很好的完成代码的解耦和工作,加强代码的可扩展性,是代码更加灵活,在不改变原有接口方法的情况下简化流程等,总结一下就是:
- 减耦合
- 增强可以替换性
- 可扩展性
- 灵活性等…
使用多态必须满足三个必要条件。
- 继承
- 重写
- 父类引用指向子类
在编写代码期间使用多态调用方法时,IDE首先会检查父类中是否有该方法,如果没有,则会编译报错。如果有父类中调用子类的同名方法。
三、一个简单的多态案例
代码如下(示例):
/**
* 支付抽象类或者接口
*/
public abstract class Pay {
abstract public String pay();
}
/**
* 支付宝支付
*/
public class AliPay extends Pay {
@Override
public String pay() {
System.out.println("支付宝pay");
return "success";
}
}
/**
* 微信支付
*/
public class WeixinPay extends Pay {
@Override
public String pay() {
System.out.println("微信Pay");
return "success";
}
}
/**
* 银联支付
*/
public class YinlianPay extends Pay {
@Override
public String pay() {
System.out.println("银联支付");
return "success";
}
}
//测试支付
public static void main(String[] args) {
//测试支付宝支付多态应用
Pay pay = new AliPay();
pay.pay();
//测试微信支付多态应用
pay = new WeixinPay();
pay.pay();
//测试银联支付多态应用
pay = new YinlianPay();
pay.pay();
}
------>输出结果如下:
支付宝pay
微信Pay
银联支付
四、多态使用的必要条件
用“鸡的各种吃法“来举个栗子
首先,我们先给出一只鸡:
class Chicken{
public void live(){
System.out.println("这是一只鸡");
}
1.子类必须继承父类
对于子类必须继承父类,是因为按照面向对象的五大基本原则所说的中的依赖倒置原则:抽象不依赖于具体,具体依赖于抽象。既然要实现多态,那么必定有一个作为"抽象"类来定义“行为”,以及若干个作为"具体"类来呈现不同的行为形式或形态。
所以我们给出的一个具体类——白切鸡类:
class BaiqieChicken extends Chicken{ }
但仅是定义一个白切鸡类是不够的,因为在此我们只能做到复用父类的属性和行为,而没有呈现出行为上的不同的形式或形态。
2.必须有重写
重写,简单地理解就是重新定义的父类方法,使得父类和子类对同一行为的表现形式各不相同。我们用白切鸡类来举个栗子。
class BaiqieChicken extends Chicken{
public void live(){
System.out.println("这是一只会被做成白切鸡的鸡");
}
}
这样就实现了重写,鸡类跟白切鸡类在live()方法中定义的行为不同,鸡类是一只命运有着无限可能的鸡,而白切鸡类的命运就是做成一只白切鸡。
但是为什么还要有“父类引用指向子类对象”这个条件呢?
3.父类引用指向子类实例
其实这个条件是面向对象的五大基本原则里面的里氏替换原则,简单说就是父类可以引用子类,但不能反过来。
当一只鸡被选择做白切鸡的时候,它的命运就不是它能掌控的。
Chicken c = new BaiqieChicken();
c.live();
运行结果:
这是一只会被做成白切鸡的鸡
为什么要有这个原则?因为父类对于子类来说,是属于“抽象”的层面,子类是“具体”的层面。“抽象”可以提供接口给“具体”实现,但是“具体”凭什么来引用“抽象”呢?而且“子类引用指向父类对象”是不符合“依赖倒置原则”的。
当一只白切鸡想回头重新选择自己的命运,抱歉,它已经在锅里,逃不出去了。
五、多态的实现途径
多态的实现途径有三种:重写、重载、接口实现,虽然它们的实现方式不一样,但是核心都是:同一行为的不同表现形式。
1.重写
重写,指的是子类对父类方法的重新定义,但是子类方法的参数列表和返回值类型,必须与父类方法一致!所以可以简单的理解,重写就是子类对父类方法的核心进行重新定义。
代码如下(示例):
class Chicken{
public void live(String lastword){
System.out.println(lastword);
}
}
class BaiqieChicken extends Chicken{
public void live(String lastword){
System.out.println("这只白切鸡说:");
System.out.println(lastword);
}
}
这里白切鸡类重写了鸡类的live()方法,为什么说是重写呢?因为白切鸡类中live()方法的参数列表和返回值与父类一样,但方法体不一样了。
2.重载
重载,指的是在一个类中有若干个方法名相同,但参数列表不同的情况,返回值可以相同也可以不同的方法定义场景。也可以简单理解成,同一行为(方法)的不同表现形式。
代码如下(示例):
class BaiqieChicken extends Chicken{
public void live(){
System.out.println("这是一只会被做成白切鸡的鸡");
}
public void live(String lastword){
System.out.println("这只白切鸡说:");
System.out.println(lastword);
}
}
这里的白切鸡类中的两个live()方法,一个无参一个有参,它们对于白切鸡类的live()方法的描述各不相同,但它们的方法名都是live。通俗讲,它们对于白切鸡鸡生的表现形式不同。
3. 接口实现
接口,是一种无法被实例化,但可以被实现的抽象类型,是抽象方法的集合,多用作定义方法集合,而方法的具体实现则交给继承接口的具体类来定义。所以,接口定义方法,方法的实现在继承接口的具体类中定义,也是对同一行为的不同表现形式。
interface Chicken{
public void live();
}
class BaiqieChicken implements Chicken{
public void live(){
System.out.println("这是一只会被做成白切鸡的鸡");
}
}
class ShousiChicken implements Chicken{
public void live(){
System.out.println("这是一只会被做成手撕鸡的鸡");
}
}
从上面我们可以看到,对于鸡接口中的live()方法,白切鸡类和手撕鸡类都有自己对这个方法的独特的定义。