Java多态

目录

①多态的理解

②多态的前提

③多态的好处

④向上转型

1)理解

2)向上转型的语法格式

3)对象向上转型的特点

4)向上转型的用处

⑤向下转型

1)为什么要有向下转型这一语法?

2)向下转型的语法格式

3)示例用来理解1)2)两点

4)instanceof关键字

作用:

语法格式:

实际用途:


①多态的理解

有了继承的铺垫,就进一步引申出了多态这一面向对象程序的又一大特点,多态是指一个类可以表现出多种形态(继承父类或实现接口而引申出多个形态的派生类)

不同对象在调用同一个方法时表现出的多种不同行为,比方说现有一个person类作为父类,和一个teacher类和student类分别作为person的子类,假设person类中有一个抽象方法名为work,这时在teacher和student类中实现work这个抽象方法时,显然应该实现不同的内容(比如teacher的work是教书,student的work是学习)


②多态的前提

  • 有继承(extends)父类或者实现(implements)接口的现象
  • 有方法的重写

示例用来理解①②两点:

abstract class person{
    abstract void work();
}
//下面两个子类,继承相同类,但实现的抽象方法的内容不同,这就体现出多态这一特点
class teacher extends person{
    void work(){
        System.out.println("老师的工作是教书");
    }
}
class student extends person{
    void work(){
        System.out.println("学生的工作是学习");
    }
}

③多态的好处

使用父类类型作为参数,可以接收所有继承自此父类的子类对象,体现出多态的拓展性和便利性

示例:

/*首先Animal.java文件中,定义了一个Animal类作为父类,
Cat类、Dog类分别作为继承Animal类的子类*/
public class Animal {
    String name;
    int age;

    Animal() {

    }

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

    void shout() {
        System.out.println(this.name + "...动物叫");
    }
}

class Cat extends Animal {
    Cat(String name, int age) {
        super(name, age);//super关键字调用父类的构造方法
    }

    void shout() {//在Cat类中重写Animal父类的shout方法
        System.out.println(this.name + "...喵喵喵");
    }
}

class Dog extends Animal {
    Dog(String name, int age) {//super关键字调用父类的构造方法
        super(name, age);
    }

    void shout() {//在Dog类中重写Animal父类的shout方法
        System.out.println(this.name + "...汪汪汪");
    }
}
/*然后是Main.java类中,用来测试,
用Animal类、Cat类、Dog类分别示例化3个对象(a,c,d),并传入参数给构造方法构造
然后分别传入a,c,d给func函数,在func函数中调用shout方法*/
public class Main {
    public static void main(String[] args) {
        Animal a = new Animal("动物名", 19);
        func(a);
        Cat c = new Cat("小猫咪", 1);
        func(c);
        Dog d = new Dog("小狗狗", 2);
        func(d);

    }
//func函数的形参可接收Animal类及其子类的对象
    public static void func(Animal a) {
        a.shout();
    }
}


//输入结果:
/*
动物名...动物叫
小猫咪...喵喵喵
小狗狗...汪汪汪
*/

通过上面这个示例,可以体会到多态的好处,父类类型作为func函数的参数,可以接收所有此类的子类对象,如果没有这一特点,对于func函数,我们就需要编写3个互相重载的函数(即函数的参数列表不同)


④向上转型

1)理解

向上转型是Java多态的一个重要语法现象,正常来说用类实例化一个对象时,应该写成

A类型 对象名 = new A类型();

的格式,当类碰上继承,就出现了接下来->2)这种情况:

2)向上转型的语法格式

  • 子类类型 子类对象 = new 子类类型();父类类型 上转型对象 = 子类对象;
  • 父类类型 上转型对象 = new 子类类型();

//上面两种写法是一样的,只不过绿色这种是实例化对象和转型同步操作,即"出生就向上转型"

用子类实例化一个对象,但引用给父类对象,这就是向上转型

可以理解为把小范围数据类型赋值给大范围数据类型,如把short类型变量s赋值给int类型变量i,

即:

short s = 27;

int a = b;

3)对象向上转型的特点

向上转型的对象——

  • 调用的变量是父类中的变量,且父类中必须有该变量,否则编译报错
  • 调用的方法是子类中的方法,且该方法一定是从父类中继承过来的(可重写)
  • 不能调用子类中独有的方法(这一点和上一点相互照应)!
  • 自编顺口溜(*^_^*)->"变量只用老子的,方法先看儿子的,儿子独有的不用"

示例用来理解:

public class Main {
    public static void main(String[] args) {
        //父类类型 父类对象 = new 子类类型();
        Game g1 = new Gensin();//g1是Gensin类对象,向上转型成Game类对象
        LOL g = new LOL();
        Game g2 = g;//g2是LOL类对象,向上转型成Game类对象

        System.out.println("对象g1访问到的name是:" + g1.name + ";对象g2访问到的name是:" + g2.name);//访问name变量,访问的变量一定是Game父类中的
        g1.func();//这个方法是Gensin子类继承并重写的,调用的方法是Gensin子类中重写后的
        g2.func();//这个方法是LOL子类继承但没重写的,调用的方法是Game父类继承给LOL子类的
        //g1.func_Gensin();//这个方法是Gensin子类独有的,g1无法调用!
    }

}

class Game {
    String name = "游戏";

    void func() {
        System.out.println("游戏...启动");
    }
}

class Gensin extends Game {
    String name = "原神";

    void func() {//重写Game父类的func方法
        System.out.println("原神...启动!");
    }

    void func_Gensin() {//写一个Gensin类独有的方法(即该方法不是继承的)
        System.out.println("原来,你也玩原神?");
    }
}

class LOL extends Game {
    String name = "英雄联盟";
    //这里没有重写Game中的func方法
}

4)向上转型的用处

说实话,本人自己在初学多态这一块时,也很懊恼,感觉关系很杂乱,理了很久甚至还编了上面那个绿色的顺口溜,才大概理清。为什么要有向上转型这种语法呢,向上转型有什么实际用处?

先来看一段示例代码:

class Animal {
    void work() {
        System.out.println("上帝认为,不同的动物应该有不同的工作");
    }

    void eat() {
        System.out.println("上帝认为,不同的动物应该吃不同的食物");
    }

    void play() {
        System.out.println("上帝认为,不同的动物喜欢玩不一样的玩具");
    }
}

class Dog extends Animal {
    void work() {
        System.out.println("看家(不是拆家!)");
    }

    void eat() {
        System.out.println("小狗吃狗粮");
    }

    void play() {
        System.out.println("小狗爱用嘴接飞盘");
    }
}

class Cat extends Animal {
    void work() {
        System.out.println("抓小脑鼠");
    }

    void eat() {
        System.out.println("小猫吃猫粮");
    }

    void play() {
        System.out.println("小猫爱用爪爪摸球");
    }
}

假如你开了一家宠物店,有一个孤寡老人在家太孤独,想买一只小动物陪自己,并顺便完成一些工作,比如说买小狗可以看家,买小猫咪可以抓老鼠,还得准备一些生活用品,比如小狗要吃狗粮,小猫要吃猫粮。

对应的代码就有两种实现思路:

public class Main {
    public static void main(String[] args) {
        //思路1:常规做法
        Dog d = new Dog();
        d.work();
        d.eat();
        d.play();
        //思路2:向上转型
        Animal a = new Dog();
        a.work();
        a.eat();
        a.play();
    }

}

听说老人要买小狗,你狗粮、狗链、飞盘什么跟狗相关的杂七杂八的东西都准备好了,突然这时老人改变想法想买猫了(像极了你的甲方?),

这个时候,如果你用的是思路1,那你可惨了,你得改掉用Dog实例化的对象d,以及用它调用的3个方法。如果你用的是思路2,你就能优雅的开一瓶82年的拉菲,优雅地把示例化对象的那行代码的Dog类改成->Cat类,然后优雅地下班(*^_^*)

来对比修改需求后的代码:

public class Main {
    public static void main(String[] args) {
        //常规做法
        Cat c = new Cat();//示例化的对象要改成Cat类实例化的c
        c.work();
        c.eat();
        c.play();//这三行原本用d调用的,都得改成c来调用
        //向上转型
        Animal a = new Cat();//实例化的子类对象改成Cat
        a.work();
        a.eat();
        a.play();//这三行完全不用管
    }
}

你可能觉得改几个名字算不上什么麻烦,当然这只是一个小栗子,实际开发中如果你只会单纯地用一般类去实例化对象,总有一天会来一个需求多变的甲方折磨你的(恶人必被恶人磨hhh


⑤向下转型

1)为什么要有向下转型这一语法?

上面花了那么大篇幅讲了多态的好处,其实多态还有一个坏处,而且这个坏处前面已经涉及到了->利用多态向上转型这一语法实例化的对象,不能调用子类中独有的方法!如果你忘了这一点,可以回想我顺口溜"变量只用老子的,方法先看儿子的,儿子独有的不用"的第三句话->儿子独有的不用

这个弊端如果不能解决,向上转型的子类只能调用继承父类的方法(可重写),就会限制子类的扩充性、丰富性

向下转型就是用来解决这一弊端的!

看示例:

class Animal {
    void work() {
        System.out.println("上帝认为,不同的动物应该有不同的工作");
    }

}

class Dog extends Animal {
    void work() {
        System.out.println("看家(不是拆家!)");
    }
    void swim(){
        System.out.println();
    }
}

class Rabbit extends Animal {
    void work() {
        System.out.println("我想不到兔子能干什么...将就一下");
    }
}

上述代码中,定义了一个Animal类作为父类,Dog类和Rabbit类作为子类,两个子类都对Animal这个父类的work方法重写了,其中Dog类有一个单独的swim方法(冷知识,兔子很怕水,下水容易寄,所以swim这个方法如果写父类里继承给兔子的话,显然不合适吧?所以单独写在Dog类里)

如果利用多态向上转型的语法,去实例化对象a,然后调用swim方法,显然行不通

public class Main {
    public static void main(String[] args) {
        Animal a = new Dog();
        a.swim();
        //报错,swim这个方法在父类Animal中不存在,a是Dog向上转型对象,只能调用继承自父类的方法
    }
}

2)向下转型的语法格式

首先,向下转型必须先出现向上转型,所以一定先包含向上转型的语法格式:

  • 父类类型 上转型对象1 = new 子类类型A();//这一行先是向上转型
  • 子类类型A 下转型对象 = (子类类型A)上转型对象1;

注意点:

  1. 必须先向上转型,子类类型A 子类对象 = (子类类型A)new 父类类型();//这种写法是错误的,因为右边new出来的对象没有经过向上转型
  2. 第2句的括号(子类类型A)不可以省略

    可以理解为把大范围数据类型赋值给小范围数据类型,如把int类型变量i赋值给short类型变量s,

    即:

    int i = 10;

    short s = (short)i;//这个括号显式强转在Java中是不可省略的!

  3. 第2句的括号(子类类型A)必须和第一句的new 子类类型A对应,这很好理解->假如第一句是Dog类示例化的对象向上转型成Animal类,那么第二句也得是向下转型为Dog类而不是Cat类或Rabbit类(物种突变!?

3)示例用来理解1)2)两点

代码续接⑤/1)

public class Main {
    public static void main(String[] args) {
        Animal a = new Dog();//先向上转型
        Dog dog = (Dog) a;//再向下转型
        dog.swim();//成功调用swim方法

        Rabbit rabbit = (Rabbit) a;
        //运行报错,原因:a是由Dog类示例化的对象向上转型得到的,无法向下转型为Rabbit类!
    }
}

4)instanceof关键字

作用:

instanceof关键字可以用来判断某个对象是否属于类(包括其父类)

语法格式:

对象 instanceof 类

  • 情况1:如果instanceof左边的对象确实属于右边的类,那么整体结果为true
  • 情况2:否则结果就是false
实际用途:

在运用向下转型语法时,可以利用instanceof关键字配合if条件语句来避免(减少)转型出错的情况

示例:那么⑤/3)中的代码就可以优化成如下代码,增强代码的健壮性——

public class Main {
    public static void main(String[] args) {
        Animal a = new Dog();//先向上转型
        if (a instanceof Dog) {
            Dog dog = (Dog) a;//再向下转型
            dog.swim();//成功调用swim方法
        } else if (a instanceof Rabbit) {
            Rabbit rabbit = (Rabbit) a;
        }
    }
}

写了好久,终于可以睡觉了┭┮﹏┭┮

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java多态指的是同一个方法名可以根据不同的对象调用出不同的行为。具体来说,多态是一种面向对象编程的特性,实现多态的方式主要有两种:方法重载和方法覆盖。方法重载指的是在一个类中定义多个同名方法,但这些方法有不同的参数列表,编译器会根据参数列表的不同选择合适的方法进行调用。方法覆盖指的是子类重写父类的方法,使得在使用父类对象调用该方法时,实际调用的是子类中的方法。 多态的好处在于,它可以提高代码的灵活性和可扩展性。通过多态,我们可以为不同的对象提供不同的行为,从而使得程序更加具有扩展性。此外,多态还可以让程序的调用更加简洁、清晰,提高了代码的可读性和可维护性。 下面是一个简单的Java多态的例子: ```Java class Animal { public void makeSound() { System.out.println("动物发出声音"); } } class Cat extends Animal { public void makeSound() { System.out.println("猫发出“喵喵”的声音"); } } class Dog extends Animal { public void makeSound() { System.out.println("狗发出“汪汪”的声音"); } } public class PolymorphismExample { public static void main(String[] args) { Animal animal1 = new Animal(); Animal animal2 = new Cat(); Animal animal3 = new Dog(); animal1.makeSound(); // 动物发出声音 animal2.makeSound(); // 猫发出“喵喵”的声音 animal3.makeSound(); // 狗发出“汪汪”的声音 } } ``` 在上面的例子中,Animal是一个父类,Cat和Dog是Animal的子类。Animal类中定义了一个makeSound()方法,Cat和Dog类分别重写了这个方法。在main()方法中,我们创建了三个Animal类型的对象,并分别调用它们的makeSound()方法。由于animal2和animal3都是Animal类型的对象,但实际上它们分别是Cat和Dog类型的对象,因此在调用它们的makeSound()方法时,实际上是调用了它们各自的实现,也就是Cat类和Dog类中重写的makeSound()方法。这就是Java多态的表现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值