多态概念
通俗来说,就是多种形态,具体点就是去完成某个“行为”,当不同的对象去完成时会产生出不同 的状 态。
多态实现的条件
1必须在继承体系下
2子类必须要对父类中方法进行重写
3通过父类的引用调用重写的方法
重点:******多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法*******
要想弄懂多态就得先弄懂向上转型和方法得重写和动态绑定
向上转型
向上转型:实际就是创建一个子类对象,将其当成父类对象来使用。
语法格式:父类类名 父类引用名=new 子类类型();
三种实现方式:
1直接赋值
其实第一种直接赋值是非匿名的,非匿名的对象可以重复用,指代的都是同一个,但是第二种就是匿名对象,只能用一次,下一次再new时候又是另外一个,非同一个对象2方法传参2方法传参2方法传参:调用func方法时候将子类对象以参数的形式传过去
从结果上看---->其实这里就相当于"直接赋值",就是把cat给到animal这个形参,那不就等于
只是说这里手段变了,实质还是一样的。一开始是直接给,直接赋,现在是间接“传”,这里可能会有点难理解。
3方法返回
方法重写
重写(override):也称为覆盖 覆写。重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等 实现过程(方法体)进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定 于自己的行为。 也就是说子类能够根据需要实现父类的方法
方法重写要求:1方法名一样 2方法参数列表一样(类型,个数,顺序)3返回值类型相同(被重写的方法返回值类型可以不同,但是必须是具有父子关系的)
注意:1:
2:父类被static、private修饰的方法、构造方法都不能被重写
3重写的方法, 可以使用 @Override 注解来显式指定. 有了这个注解能帮我们进行一些合法性校验. 例如不小心 将方法名字拼写错了 (比如写成 aet), 那么此时编译器就会发现父类中没有 aet 方法, 就会编译报错, 提示无法 构成重写.
快速实现方法重写
重写和重载的区别
重写的要求比重载更加严格,重写的设计初衷对于已经投入使用的类,尽量不要进行修改。最好的方式是:重新定义个新的类,“来一重复利用其中共性的内容”, 并且添加或者改动新的内容
现象:但是:
其实这里发生了动态绑定
动态绑定:也称为后期绑定(晚绑定),即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体 调用那个类的方法。在这里,animal在编译的时候是不能确定具体要调用父类亦或者是子类的eat,但当程序运行时,如果子类中对父类中的方法有重写,则一定会掉用子类中重写的eat方法,这个过程叫做动态绑定。
说白了:就是通过父类引用调用子类中被重写的方法的这样的一个过程就是动态绑定。
静态绑定:也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用那个方法。典型代表方法重载。
有了动态绑定,向上转型,方法重写,我们就能来理解多态了
我们再来看看多态实现的条件,就能理解记忆了
1. 必须在继承体系下(是在继承体系下的吧?有父类,子类,不然肯定不能重写父类方法)
2. 子类必须要对父类中方法进行重写(对父类的方法进行了重写吧?,以便进行动态绑定对吧)
3. 通过父类的引用调用重写的方法(用向上转型将子类对象交给父类引用,然后根据引用对象不同,调用不同的eat方法,是父类的引用调用子类的重写方法了吧?)
多态体现:在代码运行时,当传递不同类对象时,会调用对应子类中的重写方法
向下转型
为什么要下向下转型呢?我们知道,向上转型的结果就是把子类对象当作父类对象来用,那么我们通过相应的父类引用如animal,如果没有方法重写,进行动态绑定,那么我们就只能访问父类成员了,在一些情况下要求访问子类成员的时候就不行了,此时:由于父类引用接收了上转的子类对象,将父类引用还原为子类对象即可,即向下转型。
为了向下转型时候更加安全,引入关键字instance of 如果表达式为true,则可以安全转换
多态的优缺点
【使用多态的好处】 1. 能够降低代码的 "圈复杂度", 避免使用大量的 if - else如
什么叫 "圈复杂度" ? 圈复杂度是一种描述一段代码复杂程度的方式. 一段代码如果平铺直叙, 那么就比较简单容易理解. 而如 果有很多的条件分支或者循环语句, 就认为理解起来更复杂. 因此我们可以简单粗暴的计算一段代码中条件语句和循环语句出现的个数, 这个个数就称为 "圈复杂度". 如果一个方法的圈复杂度太高, 就需要考虑重构. 不同公司对于代码的圈复杂度的规范不一样. 一般不会超过 10
优化过后
2. 可扩展能力更强 如果要新增一种新的形状, 使用多态的方式代码改动成本也比较低.
比如要新增一个形状,只需要子类继承父类就可以了
多态缺陷:代码的运行效率降低。
1. 属性没有多态性 当父类和子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性
2. 构造方法没有多态性
避免在构造方法中调用重写的方法 如下这段代码
因为先进行父类的构造方法,子类中的
private int num = 1是子类实例 这时还没到这来,所以num还是数据类型的默认值0,所以在调用func并且进行打印num时显示的就是0了。
所以在构造方法内,尽量避免使用实例方法。用尽量简单的方式使对象进入可工作状态", 尽量不要在构造器中调用方法(如果这个方法被子类重写, 就会触 发动态绑定, 但是此时子类对象还没构造完成), 可能会出现一些隐藏的但是又极难发现的问题