JAVA面向对象之多态

面向对象之多态

例子

class Person{}

class Boy extends Person{}

class Girl extends Person{}

一、多态的定义

生活上:

  • 通俗的讲,就是同一个东西表现出多种状态
  • 比如我开头的例子,男孩,女孩都是人类。是人类的不同状态

程序上:

  • 父类引用指向子类的实例
  • 同一个引用类型,使用不同的实例而执行不同操作
  • 当我们使用父类的引用,指向子类的实例的时候,实际上就是一个向上转型的过程

我们一般在创建对象的时候

Boy boy = new Boy();

Girl girl = new Girl();

而多态的创建

//父类的引用指向子类的对象
//前面是父类声明 = 新建一个子类的对象
Person p = new Boy();

我们可以直接输出这个父类看一下

Person p = new Boy();
System.out.println(p);

运行结果:

Boy@2503dbd3

发现我们运行的时候识别到了它是Boy类

也就是说我们在编译前它被识别为父类,而编译后也就是运行的时候就被识别为子类了

这就是多态

在编译的时候创建父类引用指向子类的对象

而特性是运行的时候就已经是子类的对象了

二、为什么使用多态

我们来看这样一个场景

我们人都是要吃饭的,都有吃饭这个行为

class Person {
    public void eat(){
        System.out.println("是人就得吃饭");
    }
}

虽然人都得吃饭,但是针对到男孩,女孩在吃饭上是有一定的差异的。

就需要重写一下父类的方法了

//男孩 
class Boy extends Person{
    public void eat(){
        super.eat();
        System.out.println("男孩要力气,得吃肉");
    }
}
//女孩
class Girl extends Person{
     public void eat() {
         super.eat();
         System.out.println("女孩要苗条,得少吃");
     }
 }

我们假设一个食堂类,食堂里是男孩,女孩吃饭的地方

class Canteen{
    public void meal(Boy boy){
        System.out.println("到食堂干饭");
        boy.eat();
    }

    public void meal(Girl girl){
        System.out.println("到食堂干饭");
        girl.eat();
    }
}

那么我们想要男孩,女孩都能进去吃饭,我们就需要进行重载,才能识别所有的参数

测试代码:

Canteen c = new Canteen();
Boy boy = new Boy();
c.meal(boy);

但是这样就会有一个问题

我们这里是只有男孩和女孩这两个类,重载了一次,代码开始有些重复了。

我们之前说过代码重复,我们就需要找到合适的方法解决重复的代码

为什么使用多态

  • 为了解决重复代码
  • 为了完善项目结构

三、如何实现多态

我们一开始说过,父类引用指向子类的对象是多态,该如何运用呢

Person p = new boy();
p.eat();

我们发现调用的是子类重写父类的方法

实际上多态的使用就这么简单

  • 系统会自动进行类型转换

  • 通过父类引用变量调用的方法是子类覆盖或继承的子类方法,不是父类的方法

  • 通过父类引用变量无法调用子类特有的方法

1、通过方法参数

将我们参数从子类改为父类

代码如下

class Canteen{
    public void meal(Person person){
        System.out.println("到食堂干饭");
        boy.eat();
    }
}

我们测试传参,测试代码不需要更改

Canteen c = new Canteen();
Boy boy = new Boy();
c.meal(boy);
//测试的参数不需要改变

也是能够实现的,但是不需要重载

这个能够实现是因为形参Person person就是创建了一个父类的引用

当我们传参的时候,就是使父类指向了子类的对象

2、通过方法的返回值

这个就更简单了

我们先写一个方法

代码如下

public Person getPerson(String choose){
    if(choose.equals("boy")){
        return new Boy();
    }else if(choose.equals("girl")){
        return new Girl();
    }else{
        System.out.println("输入有误");
        return null;
    }
}

当我们调用该方法的时候,通过参数选择,返回男孩或者女孩。

返回的都是一个Person类型的,但是却是一个父类的引用指向子类的对象

四、注意点

1、转型问题

刚才说过,父类引用指向子类的对象,是向上转型。

//左边是父类  右边是子类
//和以前学习基础类型时候的默认强转很像
//int a = short
Person p = new Boy();

有向上转型就有想下转向

//向下转型的注意点是:能被转为子类的父类,一定是父类引用指向子类的对象
//能转成功,也必须是父类指向的就是这个子类才行
//同样和基础类型的强转很像
//short a = (short)int
Boy b =(Boy)p;

2、确保转型成功--instanceof关键字

我们是已知当前这个对象是父类指向的某个子类

所以直接能够将父类向下转型为子类

但是我们可以看下面代码

Person p = new Boy();
Girl g = (girl)p;

运行之后就告诉我们,是没办法将Boy类转为Girl类

我们明显的发现一定需要转为对应的子类才能成功。

所以我们需要一些判断手段

boolean a = p instanceof Boy;
//我们发现返回的是true

由此我们可以看出来

[对象名] instanceof [类];
//判断对象是否是该类的
//是返回true 否返回false

这样,我们就可以直接先判断再强转了

if(p instanceof Boy){
    Boy b =(Boy)p;
}

可保万无一失

3、为什么需要强转

我们本来今天就是为了使用父类引用子类的对象去操作,为什么还需要进行强转回去呢

我们发现,使用多态没有办法调用子类独有的方法,当我们需要调用子类独有的方法的时候,我们进行强转然后再调用

五、面向对象小结

面向对象的三大特性:封装、继承、多态

  • 封装是隐藏对象的属性和实现细节

    • 将类的成员属性声明为私有的,同时提供公有的方法实现对该成员属性的存取操作
  • 继承是软件可重用性的一种表现

    • 新类可以在不增加自身代码的情况下,通过从现有的类中继承其属性和方法充实自身内容
  • 多态是具有表现多种形态的能力的特征

    • 在程序设计的术语中,意味着一个特定类型的变量可以引用不同类型的对象,自动地调用引用的对象的方法
      • 即根据作用到的不同对象类型,响应不同的操作

JAVA面向对象之多态

 

上一篇:“护网”行动安全防守工作指南 - 文章转自Ill BOY的博客


下一篇:JAVA一个类中可以有什么?