多态(polymorphic)
目录
1. 多态的基本介绍
2. 多态实现条件
3. 重写
重写的介绍:
【重写和重载的区别】
动、静态绑定机制
5 向上转型和向下转型
向上转型
向上转型的特点(总结):
向下转型
多态的优缺点
多态是Java三大基本特征中最抽象也是最重要的特征。多态是建立在封装和继承衍生之上的。
1. 多态的基本介绍
多(多种)态(状态)。 多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同 的状态。
举个例子:动物有很多种类,狗、猫等等,在吃这个条件下;猫吃猫粮,狗吃狗粮。这就是多态的具体体现。
总之就是:同一件事情,发生在不同对象身上,就会产生不同的结果。
2. 多态实现条件
在java中要实现多态,必须要满足如下几个条件,缺一不可:
1. 必须在继承体系下
2. 子类必须要对父类中方法进行重写
3. 通过父类的引用调用重写的方法
多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法。
案例:
public class demo {public static void main(String[] args) {// 编译器在编译代码时,并不知道要调用Dog 还是 Cat 中eat的方法// 等程序运行起来后,形参a引用的具体对象确定后,才知道调用那个方法// 注意:此处的形参类型必须时父类类型才可以Animal dog = new Dog();dog.eat();Animal cat = new Cat();cat.eat();}
}
class Animal {public void eat() {System.out.println("吃饭");}
}
class Dog extends Animal {public String name;public void bark() {System.out.println("汪汪汪!");}
}
class Cat extends Animal {public void miaow() {System.out.println("喵喵喵!");}
}
这个就是一个多态的向上转型,Dog dog = new Dog() ; 这没问题,而现在dog的类型为Animal了,类型不一样;理论上来说类型不一样的不可以这么赋值,为什么这里可以写,那是因为二者构成了父子类关系。
那现在我们去试着调用子类中特有的方法:
编译似乎不然通过,这是为什么呢?
当我们的 " . " 之前的类型里,是必须包含 " . " 之后的成员或方法,否则编译不通过。当我们调用bark的时候,Animal中找不到Dog指向的bark,所以无法运行。
结论: 当我们发生向上转型的时候,我们只能调用父类有的成员和方法,不能调用子类特有的成员和方法。
向上转型是多态发生的基础。
还有其他向上转型的其他例子:
public static void func(Animal animal){}public static void main(String[] args) {Dog dog = new Dog();func(dog);}
public static Animal func(){return new Dog();}public static void main(String[] args) {Dog dog = new Dog();func();}
3. 重写
我们上面猫和狗吃饭有点不太符合我们的实际需求,狗就吃狗粮,猫就吃猫粮,并且我不想在父类中对eat进行重写。
我们将代码进行修改:
public class demo {public static void main(String[] args) {Animal dog = new Dog();dog.name = "小金毛";dog.eat();Animal cat = new Cat();cat.name = "小折耳";cat.eat();}
}
class Animal {public String name;public int age;public void eat() {System.out.println("吃饭");}
}
class Dog extends Animal {public void bark() {System.out.println("汪汪汪!");}@Overridepublic void eat() {System.out.println(name + "吃狗粮");}
}
class Cat extends Animal {public void miaow() {System.out.println("喵喵喵!");}@Overridepublic void eat() {System.out.println(name + "吃猫粮");}
}
注意看:
子类中有一个与父类同名的方法上面一行有一个 " @Override " 这个就是注解,注解有很多中,这里的 " @Override " 是指对重写的方法进行检查,检查你是否发生了重写,如果没有发生会报错。
重写的介绍:
重写(override):也称为覆盖。重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程
进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定
于自己的行为。 也就是说子类能够根据需要实现父类的方法。
【方法重写的规则】
1. 子类在重写父类的方法时,一般必须与父类方法原型一致: 返回值类型 方法名 (参数列表) 要完全一致
2. 被重写的方法返回值类型可以不同,但是必须是具有父子关系的
3. 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类方法被public修饰,则子类中重写该方法就不能声明为 protected
4. 父类被static、private修饰的方法、构造方法都不能被重写。
5. 重写的方法, 可以使用 @Override 注解来显式指定. 有了这个注解能帮我们进行一些合法性校验. 例如不小心将方法名字拼写错了 (比如写成 aet), 那么此时编译器就会发现父类中没有 aet 方法, 就会编译报错, 提示无法构成重写.
【重写和重载的区别】
区别点 | 重写(override) | 重载(override) |
参数列表 | 一定不能修改 | 必须修改 |
返回类型 | 一定不能修改【除非可以构成父子类关系】 | 可以修改 |
访问限定符 | 一定不能做更严格的限制(可以降低限制) | 可以修改 |
动、静态绑定机制
至于重写是怎么发生的,这里需要讲一个动态绑定机制。
Java的动态绑定机制(非常重要):
1. 当调用对象方法时,该方法会和该对象的内存地址(运行类型)绑定
2. 当调用对象属性时,没有动态绑定机制,哪里声明,那里使用。
动态绑定发生条件:
1. 向上转型
2. 重写
3. 通过父类引用调用父类和子类重写的方法
静态绑定:也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用那个方法。典型代表:方法的重载。
5 向上转型和向下转型
向上转型
向上转型的本质:父类的引用指向了子类的对象。
语法:父类类型 对象名 = new 子类类型()
例如:
Animal animal = new Cat("元宝",2);
animal是父类类型,但可以引用一个子类对象,因为是从小范围向大范围的转换。
猫属于动物,但是动物不仅仅只有猫。
【使用场景】
1. 直接赋值
2. 方法传参
3. 方法返回案例不做演示,和上面的案例差不多。
向上转型的特点(总结):
1. 可以调用父类的所有成员,但是需要遵守权限。
2. 不能调用子类特有的成员、方法。//因为在编译阶段,能调用哪些成员是由编译类型决定的
3. 最终运行效果看子类的具体表现,即调用时从子类开始查找方法,然后调用。
向上转型的优点:让代码实现更简单灵活。
向上转型的缺陷:不能调用到子类特有的方法。
向下转型
语法:子类类型 引用名 = (子类类型) 父类引用
例如: Cat cat = (Cat) animal;
要求:父类的引用必须指向的是当前目标类型的对象。
向上转型是在堆上创建一个子类的对象把地址赋给了父类的引用,向下转型就是这个父类的引用把开辟的子类的空间的地址回到了子类的引用,所以可以使用子类独有的方法。
将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的方法,此时:将父类引用再还原为子类对象即可,即向下转换。
public class TestAnimal {public static void main(String[] args) {Cat cat = new Cat("元宝",2);Dog dog = new Dog("小七", 1);
// 向上转型Animal animal = cat;animal.eat();animal = dog;animal.eat();
// 编译失败,编译时编译器将animal当成Animal对象处理
// 而Animal类中没有bark方法,因此编译失败
// animal.bark();
// 向上转型
// 程序可以通过编程,但运行时抛出异常---因为:animal实际指向的是狗
// 现在要强制还原为猫,无法正常还原,运行时抛出:ClassCastExceptioncat = (Cat)animal;cat.mew();
// animal本来指向的就是狗,因此将animal还原为狗也是安全的dog = (Dog)animal;dog.bark();}
}
向下转型用的比较少,而且不安全,万一转换失败,运行时就会抛异常。Java中为了提高向下转型的安全性,引入了 instanceof ,如果该表达式为true,则可以安全转换。
public class TestAnimal {public static void main(String[] args) {Cat cat = new Cat("元宝",2);Dog dog = new Dog("小七", 1);
// 向上转型Animal animal = cat;animal.eat();animal = dog;animal.eat();if(animal instanceof Cat){cat = (Cat)animal;cat.mew();} if(animal instanceof Dog){dog = (Dog)animal;dog.bark();}}
}
instanceof 关键词官方介绍:Chapter 15. Expressions
多态的优缺点
假设有如下代码:
class Shape {//属性....public void draw() {System.out.println("画图形!");}
}
class Rect extends Shape{@Overridepublic void draw() {System.out.println("♦");}
}
class Cycle extends Shape{@Overridepublic void draw() {System.out.println("●");}
}
class Flower extends Shape{@Overridepublic void draw() {System.out.println("❀");}
}
【使用多态的好处】
1. 能够降低代码的 "圈复杂度", 避免使用大量的 if - else
什么叫 "圈复杂度" ?
圈复杂度是一种描述一段代码复杂程度的方式. 一段代码如果平铺直叙, 那么就比较简单容易理解. 而如果有很多的条件分支或者循环语句, 就认为理解起来更复杂.
因此我们可以简单粗暴的计算一段代码中条件语句和循环语句出现的个数, 这个个数就称为 "圈复杂度".
如果一个方法的圈复杂度太高, 就需要考虑重构.
不同公司对于代码的圈复杂度的规范不一样. 一般不会超过 10
例如我们现在需要打印的不是一个形状了, 而是多个形状. 如果不基于多态, 实现代码如下:
public static void drawShapes() {Rect rect = new Rect();Cycle cycle = new Cycle();Flower flower = new Flower();String[] shapes = {"cycle", "rect", "cycle", "rect", "flower"};for (String shape : shapes) {if (shape.equals("cycle")) {cycle.draw();} else if (shape.equals("rect")) {rect.draw();} else if (shape.equals("flower")) {flower.draw();}}}
如果使用使用多态, 则不必写这么多的 if - else 分支语句, 代码更简单
public static void drawShapes() {
// 我们创建了一个 Shape 对象的数组.Shape[] shapes = {new Cycle(), new Rect(), new Cycle(),new Rect(), new Flower()};for (Shape shape : shapes) {shape.draw();}
}
2. 可扩展能力更强
如果要新增一种新的形状, 使用多态的方式代码改动成本也比较低.
class Triangle extends Shape {@Overridepublic void draw() {System.out.println("△");}
}
对于类的调用者来说(drawShapes方法), 只要创建一个新类的实例就可以了, 改动成本很低.
而对于不用多态的情况, 就要把 drawShapes 中的 if - else 进行一定的修改, 改动成本更高.
多态缺陷:代码的运行效率降低。
1. 属性没有多态性
当父类和子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性
2. 构造方法没有多态性
多态(polymorphic)相关推荐
- inheritance中文Java语言_3.3Java语言面向对象的封装(Encapsulation)、继承(Inheritance)、多态(polymorphic)...
封装(Encapsulation) 封装是处理对象的一个重要概念,从形式上看,封装将数据和行为组合在一个包里面,并对对象的使用者隐藏具体的实现方式,对象的数据称为实例字段(instance field ...
- 找一个Java游戏里面有九尾狐_Javase中多态polymorphic的简单介绍
-------------多态----------------- (1)面向对象三大核心思想: 1.封装 2.继承 3.多态 (2)多态定义:父类的引用指向子类的对象. (3)引用指的是父类声明的一个 ...
- Java--多态(polymorphic),上下转型,多态好处弊端
多态(polymorphic):事物存在的多种形态 多态前提 (1)要有继承关系 (2)要有方法重写 (3)要有父类引用指向子类对象 多态好处 (1)提高了代码的维护性 ...
- C++中的封装、继承、多态
封装(encapsulation):就是将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成"类",其中数据和函数都是类的 ...
- Java编程基础10——面向对象_多态抽象类接口
1.多态的概述及其成员访问特点代码体现 A:多态(polymorphic)概述 事物存在的多种形态 B:多态前提- 1.要有继承关系 2.要有方法重写 3.要有父类引用指向子类对象. C:多态中的成员 ...
- python基础教程:多态、多继承、函数重写、迭代器详细教程
用于类的函数 issubclass(cls,class_or_tuple) 判断一个类是否继承自其他的类,如果此类cls是class或tuole中的一个派生(子类)则返回True,否则返回False ...
- C++ 面向对象(四)—— 多态 (Polymorphism)
基类的指针(Pointers to base class) 继承的好处之一是一个指向子类(derived class)的指针与一个指向基类(base class)的指针是type-compatible ...
- Golang——接口、多态、接口继承与转换、空接口、类型断言
接口是一种规范与标准,只是规定了要做哪些事情.但是具体怎么做,是实现接口的类去做的,接口只是把所有具有共性的方法定义在一起. 接口存在的意义就是用来定义规范,用来做功能的拓展 接口最大的好处是可以实现 ...
- Java面向对象基础学习笔记(构造、重载、继承、多态、抽象类、接口、模块)
Java面向对象编程包含哪些内容? 怎么理解面向对象编程? 现实生活中,我们定义了"人"的抽象概念,这就是类class,生活中的每一个具体的人就是实例instance. class ...
- Java面向对象三大特性(封装继承多态)解释及案例
文章目录 包 包基本语法 命名规则 命名规范 导入包实例 访问修饰符 面向对象编程-封装 面向对象编程-继承 super关键词 super和this的比较 方法重写/覆盖 (override) 注意事 ...
最新文章
- Java多线程复习:5(sleep、yield方法和线程优先级)
- web框架总结(django、flask)
- 【zt】我所经历的ERP项目的失败
- 安徽一个班37人考进清华北大,老师发来一则短信,家长沉默了
- 【源码解读】Screencap源码分析-基础篇
- chrome 不支持12px以下字体为题的解决
- matlab与树莓派通信
- linux 命令 记忆方法,linux 记忆命令心得
- Spring4-自动装配Beans-通过注解@Autowired在构造方法上
- Linux I2C核心、总线与设备驱动(一)
- android表白app
- linux mysql 安装测试_linux下安装MySQL - 1583651986的个人空间 - 51Testing软件测试网 51Testing软件测试网-软件测试人的精神家园...
- java扫描指定主机的端口socket服务
- 电力负荷预测数据集(2018.1-2020.12,间隔15min,10w多条,含温度、风速等天气因素特征)
- 连八股文都不懂还指望在后端混下去么
- 【macOS免费软件推荐】第1期:MuseScore
- JavaScript复习二
- Dan Pitt卸任ONF执行董事
- 财会法规与职业道德【8】
- blender 2.8的基本使用和使用形态键(Shape key)做帧动画
热门文章
- excel操作技巧:聊聊关于打印的一些事儿
- 录音转文字app有哪些?可以试试这几款录音转文字助手
- 蘑菇车联推出“AI云+OS+智能终端+传感器”车联网一体化解决方案
- java id值_Java实现数值型ID生成器
- 最简版本mysql安装_最简单的配置mysql免安装版本的方法
- DAX: DIVIDE函数 vs 除法操作符
- golang中...是什么意思?(学习笔记,不作教程)
- 世界上唯一的七星级宾馆
- 探地雷达自动处理软件
- install step0.php,新浪微博 For Discuz论坛插件 v1.3