生活中,比如动物中跑的动作,小猫、小狗和大象,跑起来是不一样的。再比如飞的动作,昆虫、鸟类和飞机,飞起来也是不一样的。可见,同一类的事物通过不同的实际对象可以体现出来的不同的形态。多态,描述的就是这样的状态。

多态: 是指同一行为,具有多个不同表现形式。

具备多态的前提:

(1)具备继承或者实现【二选一】;
(2)方法的重写【不重写,无意义】;
(3)父类引用指向子类对象【格式体现】。

多态的体现

多态体现的格式:

  父类类型 变量名 = new 子类对象;
  变量名.方法名();

注意事项:父类类型指子类对象继承的父类类型,或者实现的父接口类型。

定义父类:

public class Fu {

    public void method() {
System.out.println("父类方法");
} public void methodFu() {
System.out.println("父类特有方法");
} }

定义子类:

public class Zi extends Fu {

    @Override
public void method() {
System.out.println("子类方法");
}

定义测试类:

public class Demo {

    public static void main(String[] args) {
// 使用多态的写法
// 左侧父类的引用,指向了右侧子类的对象
Fu obj = new Zi(); obj.method();
obj.methodFu();
}
}
1、访问成员变量

定义父类:

public class Fu {

    int num = 10;

    public void showNum() {
System.out.println(num);
}
}

定义子类:

public class Zi extends Fu {

    int num = 20;

    int age = 16;

    @Override
public void showNum() {
System.out.println(num);
}
}
(1)直接通过对象名称访问成员变量

定义测试类:

public class Demo{

    public static void main(String[] args) {

        Fu obj = new Zi();

        System.out.println(obj.num); // 父:10
} }
看 “=” 左边是谁,优先用谁,没有则向上找。
(2)间接通过成员方法访问成员变量
定义测试类:
public class Demo {

    public static void main(String[] args) {

        Fu obj = new Zi();
// 子类没有覆盖重写,就是父:10
// 子类如果覆盖重写,就是子:20
obj.showNum();
} }
看该方法属于谁,优先用谁,没有则向上找。
 
现在我们将上面的测试类添加一段代码,代码如下:
public class Demo{

    public static void main(String[] args) {

        Fu obj = new Zi();
obj.showNum(); System.out.println(obj.age);
}
}

你会发现编译的时候报错了,这是为什么呢?

那是因为 “=” 左边时父类,但是父类当中没有 age 成员变量,所以编译报错。因此:

成员变量:编译看左边,运行也看左边。

1、访问成员方法
定义父类:
public class Fu {public void method() {
System.out.println("父类方法");
} public void methodFu() {
System.out.println("父类特有方法");
} }

定义子类:

public class Zi extends Fu {

    @Override
public void method() {
System.out.println("子类方法");
} public void methodZi() {
System.out.println("子类特有方法");
}
}
定义测试类:
public class Demo {

    public static void main(String[] args) {

        Fu obj = new Zi(); 

        // 父子都有,优先用子类
obj.method(); // 子类方法 // 子类没有,父类有,向上找到父类
obj.methodFu(); // 父类特有方法
} }
看 new 的是谁,就优先用谁,没有则向上找。
 
现在我们将上面的测试类添加一段代码,代码如下:
public class Demo {

    public static void main(String[] args) {

        Fu obj = new Zi();
obj.method();
obj.methodFu(); obj.methodZi();
} }

你会发现编译时直接就报错了,这是为什么呢?

那是因为 “=” 左边时父类,但是父类当中没有 methodZi 方法,所以编译报错。因此:

成员方法:编译看左边,运行看右边。

多态的好处

先看一组例子,定义父类:

public abstract class Animal {
public abstract void eat();
}
定义子类:
// 子类Cat
public class Cat extends Animal {
  @Override
public void eat() {
System.out.println("吃鱼");
}
} // 子类Dog
public class Dog extends Animal {
  @Override
public void eat() {
System.out.println("吃骨头");
}
}

如果不使用多态的方式,测试类代码如下:

public class Demo {

    public static void main(String[] args) {

        Cat cat = new Cat();
cat.eat(); Dog dog = new Dog();
dog.eat();
} }

如果使用多态的方式,测试类代码如下:

public class Demo {

    public static void main(String[] args) {

        Animal cat = new Cat();
cat.eat(); Animal dog = new Dog();
dog.eat();
} }

因此,无论右边 new 的是哪个子类对象,“=” 左边调用方法都不会发生变化。

再看一组例子,子类和父类的与上面一样,请注意测试类代码。

 如果不使用多态的方式:
public class Demo {
public static void main(String[] args) { Cat cat = new Cat();
Dog dog = new Dog(); // 调用showCatEat
showCatEat(cat); // 调用showDogEat
showDogEat(dog);
} public static void showCatEat(Cat c) {
c.eat();
} public static void showDogEat(Dog d) {
d.eat();
} }

如果使用多态的形式:

public class Demo {
public static void main(String[] args) {
// 以上两个方法, 均可以被showAnimalEat(Animal a)方法所替代而执行效果一致
showAnimalEat(cat);
showAnimalEat(dog);
} public static void showAnimalEat(Animal a) {
a.eat();
}
}

因此:

(1)由于多态特性的支持,showAnimalEat方法的Animal类型,是Cat和Dog的父类类型,父类类型接收子类对象,当然可以把Cat对象和Dog对象,传递给方法。
(2)当eat方法执行时多态规定,执行的是子类重写的方法,那么效果自然与showCatEat、showDogEat方法一致,所以showAnimalEat完全可以替代以上两方法。
(3)不仅仅是替代,在扩展性方面,无论之后再多的子类出现,我们都不需要编写showXxxEat方法了,直接使用showAnimalEat都可以完成。
所以,多态的好处体现在可以使程序编写的更简单并有良好的扩展。实际开发的过程中,父类类型作为方法形式参数,传递子类对象给方法进行方法的调用,更能体现出多态的扩展性与便利。

多态转型

多态的转型分为向上转型与向下转型两种。
定义父类:
public abstract class Animal {
public abstract void eat();
}

定义子类:

// 子类Cat
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
} public void catchMouse() {
System.out.println("猫抓老鼠");
}
} // 子类Dog
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃SHIT");
} public void watchHouse() {
System.out.println("狗看家");
}
}
1、向上转型

向上转型:多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的。

当父类引用指向一个子类对象时,便是向上转型,使用格式如下:

父类类型 变量名 = new 子类类型();

定义测试类:
public class Demo {

    public static void main(String[] args) {

        // 对象的向上转型就是:父类引用指向之类对象。
Animal animal = new Cat(); // 本来创建的时候是一只猫
animal.eat(); // 猫吃鱼 // animal.catchMouse(); // 错误写法! } }
向上转型一定是安全的,没有问题的。但是也有一个弊端:对象一旦向上转型为父类就无法调用子类原本特有的内容(如标红的代码)。
如何解决这个这个问题呢?答案是向下转型。
2、向下转型
向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。
一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。使用格式如下:
子类类型 变量名 = (子类类型) 父类变量名;
父类和子类的代码与上述一致,测试代码如下:
public class Demo {

    public static void main(String[] args) {

        Animal animal = new Cat();
animal.eat(); // 向下转型,进行“还原”动作
Cat cat = (Cat) animal;
cat.catchMouse(); // 猫抓老鼠 // 下面是错误的向下转型,错误写法!编译不会报错,但是运行会出现异常
// 本来new的时候是一只猫,现在非要当做狗
Dog dog = (Dog) animal; // java.lang.ClassCastException,类转换异常
} }

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子类特有的方法,必须做向下转型。

3、转型异常(ClassCastException)
为了避免ClassCastException的发生(向下转型已经提到过),Java提供了 instanceof 关键字,给引用变量做类型的校验,格式如下:

变量名 instanceof 数据类型

如果变量属于该数据类型,返回true;
如果变量不属于该数据类型,返回false。
所以,转换前我们最好先做一个判断,代码如下:
public class Demo {

    public static void main(String[] args) {

        Animal animal = new Dog(); // 本来是一只狗
animal.eat(); // 狗吃SHIT // 如果希望掉用子类特有方法,需要向下转型
// 判断一下父类引用animal本来是不是Dog
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.watchHouse();
}
// 判断一下animal本来是不是Cat
if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.catchMouse();
} // 实际开发的时候更多的是单独抽离出来使用
pet(new Dog());
} public static void pet(Animal animal) {
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.watchHouse();
}
if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.catchMouse();
}
} }

【Java】面向对象之多态的更多相关文章

  1. Java面向对象之多态(来源于身边的案例)

    2019年1月3日 星期四 Java面向对象之多态(来源于身边的案例) 1. 为什么要用多态? 1.1 多态是面向对象的三大特性之一 1.2 多态是基于接口设计的模型 1.3 多态具有横向扩展特性 1 ...

  2. Java面向对象的多态

    Java中多态的概念是面向对象中除封装和继承外非常重要的知识点,也是Java面向对象三大特性最后一个特性 多态其实就是指对象存在的多种形态,多态分为引用多态和方法多态 引用多态的含义就是:父类的引用可 ...

  3. java面向对象之 多态 Polymorphism

    多态(Polymorphism):用我们通俗易懂的话来说就是子类就是父类(猫是动物,学生也是人),因此多态的意思就是:父类型的引用可以指向子类的对象. 1.多态的含义:一种类型,呈现出多种状态 主要讨 ...

  4. Java面向对象特性--多态

    Java是一种面向对象的编程语言,面向对象的三大特性就是继承,封装,多态.下面细细说一说多态. 多态的定义:一个事物的多种形态,指允许不同类的对象对同一消息做出响应.即同一消息可以根据发送对象的不同而 ...

  5. JavaSE入门学习18:Java面向对象之多态

    一Java多态 多态是同一个行为具有多个不同表现形式或形态的能力. 多态性是对象多种表现形式的体现.比方我们说"宠 物"这个对象.它就有非常多不同的表达或实现,比方有小猫.小狗.蜥 ...

  6. Java 面向对象_多态

    多态图解 代码中体现多态性 父类名称 对象名 = new 子类名称(); or 接口名称 对象名 = new 实现类名称(); // 父类 public class Father { public v ...

  7. 对Java面向对象中多态的理解

    理解的要点:多态意味着父亲的变量可以指向子类对象 面向对象程序设计的三大支柱是封装.继承和多态 封装对外把相应的属性和方法实现的细节进行了隐藏.继承关系使一个子类继承父亲的特征,并且加上了一些新的特征 ...

  8. Java面向对象11——多态

    多态  package oop.demon01.demon06; ​ public class Application {     public static void main(String[] a ...

  9. Java面向对象:多态

    多态:具有表现多种形态的能力的特征(同一个实现接口,使用不同的实例而执行不同的操作) 实现多态的优点:为了方便统一调用! 实现多态的三种方式! 1:子类到父类的转换: 例: 1 Dog dog=new ...

  10. Java面向对象之多态

    多态:具有表现多种形态的能力的特征(同一个实现接口,使用不同的实例而执行不同的操作) 实现多态的优点:为了方便统一调用! 实现多态的三种方式! 1:子类到父类的转换: 例: Dog dog=new D ...

随机推荐

  1. Pycharm 常用快捷键大全

    Pycharm 常用快捷键 常用快捷键 快捷键 功能 Ctrl + Q 快速查看文档 Ctrl + F1 显示错误描述或警告信息 Ctrl + / 行注释(可选中多行) Ctrl + Alt + L ...

  2. 初识mpvue

    听说mpvue可以实现H5和小程序的同时开发  对使用过vue的选手几乎是0难度 忍不住搓搓小手手 看了文  唔~ 似乎不是很难的样子 然后实际上手操作了一下 老规矩:新建项目 npm install ...

  3. python里怎么查看数据类型

    python里怎么查看数据类型? python里可以通过type()函数来查看数据类型. Python 内置函数 Python 内置函数 Python type() 函数如果你只有第一个参数则返回对象 ...

  4. cxf 调用 .net webservice

    1.   问题背景          现在我们两套语言并行,其中必然会涉及到不同系统的相互访问. .net 的会员信息是用 webservice  提供服务的.那如何对现有 .net webservi ...

  5. PSTAT 115 Homework4 课业解析

    PSTAT 115 Homework4 课业解析 题意: 蒙特卡洛采样之拒绝采样 解析: 给定一个概率分布p(z)=p~(z)/Zp,p~(z)已知,Zp为归一化常数,为未知数.对该分布进行拒绝采样, ...

  6. Linux下部署Jenkins

    前提:已部署好JAVA 1.下载Jenkins库并添加到yum库: wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redha ...

  7. django-模板之now标签(七)

    1.在settings.py中设置成中国时区 2.index.html 3.显示

  8. 列表渲染.html

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  9. spring cloud 2.x版本 Ribbon服务发现教程(内含集成Hystrix熔断机制)

    本文采用Spring cloud本文为2.1.8RELEASE,version=Greenwich.SR3 前言 本文基于前两篇文章eureka-server和eureka-client的实现. 参考 ...

  10. 大觅网05Day

    1.Mycat概述 在此前的服务器对数据库的存储数量要求并不高的时候,被经常使用的MySql数据基本能够满足对数据存储的要求. 但随着技术的不断发展,MySql甚至Redis都无法满足现今存储数量的指 ...