java详细多态讲解

多态

多态的概念

通俗的讲,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。

多态实现条件

在JAVA中实现多态,必须满足如下几点条件

1.必须在继承体系下
2.子类必须要对父类中方法进行重写
3.通过父类的引用调用重写方法

多态的体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法。


public class Animal{
    String name;
    int age;
public Animal (String name,int  age){
     this.name=name;
     this.age=age;
}
public void eat(){
    System.out.println(name+"吃饭");
  }
class Cat extends Animl{
   public Cat(String name,int age){
   super(name,age):
}
   public void eat(){
    System.out.println(name+"吃鱼肉");
  }
class Dogextends Animl{
   public Dog(String name,int age){
   super(name,age):
}
   public void eat(){
    System.out.println(name+"吃骨头");
  }
}
public class Test{
//编译器在编译代码时并不知道调用Dog还是Cat中的eat方法
   public static void eat(animal a){
        a.eat();
}
   public static void main(String[] args){
   Dog dog=new Dog("wangwang",1);
   dog.eat();
    }
}

重写所满足的条件:
1.方法名相同
2.参数列表相同【个数,顺序,类型】
3.返回值类型相同

重写

重写是子类对父类非静态,非private修饰,非final修饰,非构造方法等的实现过程进行重新编写。

【方法重写的规则】

子类在重写父类方法时,一般必须与父类方法原型一致:返回值类型 方法名{参数列表}要完全一致。
被重写的方法返回值类型可以不同,但必须是具有父子关系
访问权限不能比父类中被重写的方法的访问权限更低。
父类被static,private修饰的方法,构造方法都不能被重写

那些情况下不能进行重写
1.被private修饰的方法
2.被static修饰的方法
3.子类的访问修饰限定符一定要大于等于父类的访问限定修饰符
4.被final修饰的方法不能被重写
5.构造方法不能被重写

【重载和重写的区别】

区别点重写重载
参数列表一定不能修改必须修改
返回类型一定不能修改可以修改
访问限定符一定不能做更严格的限制可以修改

即:方法重载是一个的多态性表现,而方法重写是子类与父类的一种多态机制

向上转型与向下转型

向上转型

【使用场景】
发生的时机
1.直接赋值: Animal animal=new Dog()
2.传参:

public static void testFunc(Animal animal){
}
public static void main(String[] args){
Dog dog=new Dog(name:"wangwang",age:1);
testFunc(dog);
}

3.返回值

public static Animal testFunc2(){
return new Dog();
}

向上转型:实际就是创建一个子类对象,将其当成父类对象来使用
语法格式:父类名称 对象名=new 子类名称

Animalanimal=new Cat(“元宝”,2):

animal是父类类型,但可以引用一个子类对象,因为是从小范围向大范围的转换。

Dog dog=new Dog("wangwang",1);
Animal animal=dog;

等价于
Aniaml animal=new Dog(name:"wangwang",age:1);
请添加图片描述

向上转型的优点:让代码实现更简单灵活
向上转型的缺陷:不能调用到子类特有的方法

向下转型

将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类方法,但有时候可能需要调用子类特有的方法,此时:将父类引用还原为子类对象即可。
请添加图片描述

public class TestAnimal{
  public static void main(String[] args){
   Cat cat=new Cat("元宝",2);
   Dog dog=new Dog("小七");
//向上转型
  Animal animal=cat;
  animal.eat();
  animal=dog;
  animal.eat();
  //编译失败,编译时将编译器将animal当成Animal对象处理
  //向上转型
  //程序可以通过编程,无法正常还原
  cat=(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("小气",3);
//向上转型
   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();
     }
   }
 }

运行时绑定/动态绑定

1.就是发生了重写
2.是通过父类引用来调用这个重写的方法

静态绑定:在程序编译的时候通过参数类型的个数,编译器可以自动推导出来你调用的是哪个参数,此时叫做静态绑定。典型代表函数重载。

动态绑定:也称为后期绑定,即在编译时,不能确定方法的行为,需要等到程序运行时,才能够具体调用哪个类的方法。

多态实现条件

1.必须在继承体系下
2.子类必须要对父类中方法进行重写
3.通过父类的引用调用重写的方法
多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法

多态的优缺点

【使用多态的好处】
1.能够降低代码的”圈复杂度“,避免使用大量的if-else

什么叫”圈复杂度“?
圈复杂度是一种描述一段代码复杂程度的方式,一段代码如果平铺直叙,那么就比较简单,而如果有很多的条件分支或者循环语句,就认为理解起来复杂。

例如我们现在需要打印的不是一个形状,而是多个形状,如果不基于多态,实现代码如下:

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{
  public void draw(){
   System.out.println(" ");
    }
}

对于类的调用者来说,只要创建一个新类的实例就可以了,改动成本比较低,而对于不用多态的情况来说。
多态的缺陷:代码的运行效率降低
1.属性没有多态性
当父类和子类都有同名属性的时候,通过父类的引用,只能引用父类自己的成员属性。

避免在构造方法中调用重写方法

class B{
  public B(){
  //do nothing
  func();
}
public void func(){
  System.out.println("B.func()");
  }
}
class D extends B{
  public  int num=1;
  public void func(){
   System.out.println("D.func()"+num);
  }
 }
 public class Test{
   public static void main(String[] args){
    D d=new D();
  }
} 
  • 构造D对象的同时,会调用B的构造方法。
  • B的构造方法中调用func方法,此时会触发动态绑定,会调用到D中的func
  • 此时D对象自身还没有构造,此时num处在未初始化的状态,值为0,如果具备多态性,num的值应为1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿在线码字

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值