目录
1.多态的概念
多种形态,也就是说去完成某个行为,当不同的对象去完成时会产生不同的状态。
同一间事情,发生在不同的对象身上,就会产生不同的结果。
2.多态实现的条件
1.必须在继承体系下
2.子类必须要对父类中方法进行重写
3.通过父类的引用调用重写的方法
多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法。
3.重写
外壳不变,内容核心重新写。
1.方法名相同
2.参数列表相同【顺序、个数类型】
3.返回值相同
tip:被final,static,private修饰的方法不可以被重写
@override 注解,重写
子类重写父类方法时,子类的方法访问修饰限定符>=父类
重写与重载的区别:
区别点 重写 重载 参数列表 一定不能修改 必须修改 返回类型 一定不能修改【除非可以构成父类关系】 可以修改 访问限定符 一定不能做更严格的限制(可以降低限制) 可以修改
静态绑定:也成为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用哪个方法。例函数重载。
动态绑定:也称为后期绑定(晚绑定),即在编译时,不能确定方法的行为,需要等到程序运行时,才能确定具体调用哪一个方法。
tip:编译的时候确实是调用了父类eat的方法,运行的时候绑定到了子类。
4.向上转移和向下转型
4.1向上转型
创建一个子类对象,将其当成父类对象来使用。
语法:父类类型 对象名 = new 子类类型( );
Animal animal = new Cat("小宝",2);
使用场景:
1.直接传参
2.方法传参
3.方法返回
public class TestPeaple { //2.方法传参:形参为父类型引用,可以接收任意子类的对象 public static void eats(Peaple p) { p.eat(); } //3.做返回值:返回任意子类的对象 public static Peaple peapleA(String var) { if ("nan".equals(var)) { return new Men("nan", 4); } else if ("nv".equals(var)) { return new Women("nv",5); }else { return null; } } public static void main(String[] args) { //1.直接赋值:子类对象赋值给父类对象 Peaple men = new Men("小李",10); Peaple women = new Women("小王",10); eats(men); eats(women); Peaple peaple = peapleA("nan"); peaple.eat(); peaple = peapleA("nv"); peaple.eat(); } }
优点:让代码实现更加简单灵活。
缺点:不能调用到子类特有的方法。
4.2向下转型
将一个子类对象经过向上转型之后当成父类方法使用,再也没有办法调用子类的方法,但有时可能需要调用子类特有的方法,此时:将父类引用再还原为子类对象即可,即向下转换。
编译报错是因为Peaple中没有sports方法,men里面有。所以就要用到向下转型,调用子类方法。
报错的原因是因为Peaple指向的是men,不是women。为了提高向下转型的安全性,引入了instanceof,如果该表达式为true,则可以安全转换。
5.多态的优缺点
5.1多态的优点
class Rect extends Shap{ @Override public void draw(){ System.out.println("菱形"); } } class Cycle extends Shap{ @Override public void draw(){ System.out.println("⭕"); } } class Flower extends Shap{ @Override public void draw(){ System.out.println("花朵"); } }
1.能够降低代码的“圈复杂度”(描述一个代码复杂程度的方式,一个代码如果有很多条件分支语句或者有很多循环语句,就认为理解起来复杂),避免使用大量的if-else
public class drawShapes { public static void main(String[] args) { Rect rect = new Rect(); Cycle cycle = new Cycle(); Flower flower = new Flower(); String[] shapes = {"Cycle", "Rect", "Rect", "Flower"}; for (String shape : shapes) { if (shape.equals("Cycle")) { cycle.draw(); }else if(shape.equals("Flower")){ flower.draw(); }else if(shape.equals("Rect")){ rect.draw(); } } } }
复杂度高。
public class drawShapes { public static void main(String[] args) { Shap rect = new Rect(); Shap cycle = new Cycle(); Shap flower = new Flower(); //向上转型 Shap[] shapes = {cycle,rect,flower,rect}; for (Shap shape:shapes) { shape.draw(); } } }
复杂度低
2.可拓展性更强。
如果新增加一种形状,使用多态的方式代码改动成本也比较低。
class tree extends Shap{ @Override public void draw(){ System.out.println("树木"); } }
5.2多态的缺点
代码的运行效率降低。
1.属性没有多态性:当父类和子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性。
2.构造方法没有多态性。
6.避免在构造方法中调用重写的方法
class A{ public A(){ func(); } public void func() { System.out.println("A.func"); } } class B extends A { private int num = 1; @Override public void func() { System.out.println("B.func()" + num); } } public class Test { public static void main(String[] args) { B b = new B(); } }
为什么会是0呢?
调用B对象时,调用了A的构造方法,A的构造方法中调用了func()方法,此时触发了动态绑定,会调用到B中的func,此时D对象分配了内存空间,但是自身没有被初始化,值为0.
tip:所以在构造方法中,尽量避免使用实例方法,除了final和private方法。