首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【面向对象编程】多态

【面向对象编程】多态

作者头像
用户11288949
发布2024-09-24 13:33:00
发布2024-09-24 13:33:00
19700
代码可运行
举报
文章被收录于专栏:学习学习
运行总次数:0
代码可运行

1.多态的概念

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

例如:

在吃东西的情况下,狗和猫不同对象完成吃东西状态不一样。

2.实现多态的条件

在java中要实现多态,必须要满足如下几个条件,缺一不可: 1. 必须在继承体系下 2. 子类必须要对父类中方法进行重写 3. 通过父类的引用调用重写的方法 多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法。

代码语言:javascript
代码运行次数:0
运行
复制
public class test1 {
    public static void main(String[] args) {
        Animal animal=new Dog("小七",5);
        animal.eat();
    }
}
class Animal{
    public String name;
    public int age;
    public Animal(String name,int age){
        this.name=name;
        this.age=age;
    }
    public void eat(){
        System.out.println(name+"吃东西");
    }
}
class Dog extends Animal{
    public Dog(String name,int age){
        super(name,age);
    }
    public void eat(){
        System.out.println(name+"在吃狗粮");
    }
}

如图所示,eat方法实现子类重写,并且通过父类的引用,将小狗的对象传入,animal则将小狗子类进行实例化。如果有多个子类,animal调用的eat方法就不一样(根据实例化的对象决定),这个过程叫多态。

3.重写

重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程进行重新编写, 返回值和形参都不能改变。重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。 【方法重写的规则】 1.子类在重写父类的方法时,一般必须与父类方法原型一致: 返回值类型 方法名 (参数列表) 要完全一致 2.被重写的方法返回值类型可以不同,但是必须是具有父子关系的 3.访问权限不能比父类中被重写的方法的访问权限更低。

代码语言:javascript
代码运行次数:0
运行
复制
protected void eat(){
        System.out.println(name+"吃东西");//父类是protect
    }
}
class Dog extends Animal{
    public Dog(String name,int age){
        super(name,age);
    }
    private void eat(){                //报错
        System.out.println(name+"在吃狗粮");
    }
}

如图,子类在重写时发生报错;

重写和重载的区别:

                           重写(override)                                                      重载(override) 参数列表            一定不能修改                                                       必须修改 返回类型            一定不能修改【除非可以构成父子类关系】        可以修改 访问限定符         一定不能做更严格的限制(可以降低限制)        可以修改

4 向上转型

向上转型:就是创建一个子类对象,把他当做父类对象来使用。

例如

Animal animal=new Dog()

这里父类类型,引用子类对象。

向上转型三种方法

1. 直接赋值:子类对象赋值给父类对象

代码如下:

代码语言:javascript
代码运行次数:0
运行
复制
  Animal animal=new Dog("小七",5);
        animal.eat();

2. 方法传参:形参为父类型引用,可以接收任意子类的对象

代码如下:

代码语言:javascript
代码运行次数:0
运行
复制
public static void func(Animal animal) {
        animal.eat();
    }
    public static void main(String[] args) {
        Dog dog=new Dog("小七",5);
        //animal.eat();
        func(dog);
    }

其实小编觉得两者本质一样。

 3. 作返回值:返回任意子类对象

 代码如下:

代码语言:javascript
代码运行次数:0
运行
复制
 public static Animal func(String name1) {
        if ("小七".equals(name1)) {
            return new Dog("小七", 1);
        }else
            return null;

    }

    public static void main(String[] args) {     
        Animal animal=func("小七");
        animal.eat();
    }

小编这里不推荐这种写法;

向上转型的特点:

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

 5.向下转型

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

代码语言:javascript
代码运行次数:0
运行
复制
 public static void main(String[] args) {
        Dog dog=new Dog("小七", 5);
        Animal animal=dog;
        animal.eat();
        animal.run();                //报错,无法调用子类特有的方法
       
    }
}

class Animal {
    public String name;
    public int age;

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void eat() {
        System.out.println(name + "吃东西");
    }
}

class Dog extends Animal {
    public Dog(String name, int age) {
        super(name, age);
    }

    public void eat() {                
        System.out.println(name + "在吃狗粮");
    }
    public void run(){
        System.out.println(name+"在奔跑");
    }
}

如上所示 ,run方法是子类特有的方法,类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法.

那么就要用到向下转型了:

代码如下:

代码语言:javascript
代码运行次数:0
运行
复制
public static void main(String[] args) {
        Dog dog=new Dog("小七", 5);
        Animal animal=dog;
        animal.eat();
        dog=(Dog)animal;
        dog.run();

dog=(Dog)animal; dog.run(); 

这样就完成了向下转型了 。

注意:向下转型不安全

例如:当狗狗类对象向上转型为父类引用之后,如果向下强制转型为猫猫的时候,将无法正常还原。

如图:

错误示范:

代码语言:javascript
代码运行次数:0
运行
复制
public static void main(String[] args) {
        Dog dog=new Dog("小七", 5);
        Cat cat=new Cat("大胖橘",3);
        Animal animal=dog;
        animal.eat();
        cat=(Cat)animal;
        cat.sleep();                //此时狗狗类没有睡觉这个方法
    }
}

class Animal {
    public String name;
    public int age;

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void eat() {
        System.out.println(name + "吃东西");
    }
}

class Dog extends Animal {
    public Dog(String name, int age) {
        super(name, age);
    }

    public void eat() {
        System.out.println(name + "在吃狗粮");
    }
    public void run(){
        System.out.println(name+"在奔跑");
    }
}
class Cat extends Animal {
    public Cat(String name, int age) {
        super(name, age);
    }
    public void eat() {
        System.out.println(name + "在吃猫粮");
    }
    public void sleep(){
        System.out.println(name+"在睡觉");
    }
}

狗狗类没有睡觉的方法 此时就会抛出异常:ClassCastException

所以向下转型用的比较少,而且不安全,万一转换失败,运行时就会抛异常。Java中为了提高向下转型的安全性,引入了 instanceof ,如果该表达式为true,则可以安全转换。

代码语言:javascript
代码运行次数:0
运行
复制
public static void main(String[] args) {
        Dog dog=new Dog("小七", 5);
        Cat cat=new Cat("大胖橘",3);
        Animal animal=cat;

        if(animal instanceof Cat){
            cat=(Cat) animal;
            cat.sleep();
        }
        if(animal instanceof Dog){
            dog=(Dog) animal;
            dog.run();
        }

 这里就提高了向下转换的安全性,即如果先将狗的类对象向上转型给父类引用时,向下转型就不能强制转为cat对象,要经过判断。

6.多态优缺点

1. 能够降低代码的 "圈复杂度

什么是,圈复杂度是一种描述一段代码复杂程度的方式. 一段代码如果平铺直叙, 那么就比较简单容易理解. 而如果有很多的条件分支或者循环语句, 就认为理解起来更复杂. 因此我们可以简单粗暴的计算一段代码中条件语句和循环语句出现的个数, 这个个数就称为 "圈复杂度。

代码语言:javascript
代码运行次数:0
运行
复制
 public static void main(String[] args) {
        Cycle cycle=new Cycle();
        Rect rect=new Rect();
        Flower flower=new Flower();
        String[] arr={"cycle","rect","rect","flower"};   //假如要打印这些图画
        for (String row: arr){
            if(row.equals("cycle")){
                cycle.draw();
            }
            if (row.equals("rect")){
                rect.draw();
            }
            if(row.equals("flower")){
                flower.draw();
            }
        }
    }
}
class Shape{
    public void draw(){
        System.out.println("画一个画");
    }
}
class Cycle extends Shape{
    public void draw(){
        System.out.println("○");
    }
}
class Rect extends Shape{
    public void draw(){
        System.out.println("◇");
    }
}
class Flower extends Shape{
    public void draw(){
        System.out.println("❀");
    }
}

这里就使用了大量的if;

但是如果我们运用了多态的思想:

代码语言:javascript
代码运行次数:0
运行
复制
 public static void main(String[] args) {
        Shape[] shapes = {new Cycle(), new Rect(), new Rect(), new Flower()};
        for (Shape shape : shapes) {
            shape.draw();
        }
    }

这里的Shape shape=new Cycle()等等。

2. 可扩展能力更强

当如果我们增加一个新的图形,直接new一个对象就行。

缺点:

1. 属性没有多态性 当父类和子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性 2. 构造方法没有多态性

7.总结

限于小编能力有限,关于多态的相关知识,可能有些总结不到位,希望各位uu能够给出宝贵意见。

如果觉得有用的话,麻烦给小编一个小小的赞吧。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-07-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.多态的概念
  • 2.实现多态的条件
  • 3.重写
  • 4 向上转型
  •  5.向下转型
  • 6.多态优缺点
  • 7.总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档