Java基础篇_多态

目录

一、多态

1.1 多态的概念

1.2 多态实现条件

1.3 重写

1.4 向上转型

1.5 向下转型

1.6 静态与动态绑定

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

总结


一、多态

1.1 多态的概念

多态,即多种形态,就是当不同的对象去完成某个行为时,会产生出不同的状态。例如,动物都会吃饭,但是不同的动物吃的又不同,熊猫吃竹子、兔子吃胡萝卜、小羊吃草等等。所以动物都会吃饭,却吃着不同的食物,就可以理解为多态。

1.2 多态实现条件

在 Java 中要实现多态,必须要满足以下三个条件,缺一不可:

1、必须在继承体系下

2、子类必须要对父类中方法进行重写

3、通过父类的引用调用重写的方法

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

1.3 重写

重写,也称覆盖,是发生多态的条件之一。重写是子类对父类中部分方法的实现过程进行重新编写,重写的好处在于子类可以根据需要,定义特定于自己的行为。 简单来说,子类能够根据需要实现父类的方法。

【重写的基本要求】

1、方法名一样

2、参数列表(数据类型、参数个数、顺序)一样

3、返回值一样

简而言之,返回值类型 方法名 (参数列表) 要完全一致。如下图中,父类 Animal 类与子类 Dog 类中的 eat() 方法,即是构成方法的重写。

重写的规则】

1、父类中被 staticprivate、final 修饰的方法、构造方法都不能被重写。

例如:下图中父类eat()方法被final修饰,子类无法重写该方法,该方法被称为密封方法。

2、重写的方法可以使用 @Override 注解来显式指定,有了这个注解能帮我们进行一些合法性校验。

例如:下图中不小心将方法名字拼写成 eat那么此时编译器就会发现父类中没有 eat 方法就会报错提示无法构成重写。

3、子类重写父类方法时,子类的方法访问限定修饰符得 >= 父类。

例如:下图中父类方法被public修饰,若子类中重写该方法声明为 protected,则报错。

4、被重写的方法与重写的方法返回值类型可以不同,但是必须是具有父子关系。

例如:下图中两个eat()方法返回类型具有父子关系,则重写成功。

重写的设计原则

对于已经投入使用的类,尽量不要进行修改。最好的方式是:重新定义一个新的类,来重复利用其中共性的内容,并且添加或者改动新的内容。

例如:若干年前的手机,只能打电话,发短信,来电显示只能显示号码,而今天的手机在来电显示的时候,不仅仅可以显示号码,还可以显示头像,地区等。在这个过程当中,我们不应该在原来老的类上进行修改,因为原来的类,可能还在有用户使用,正确做法是:新建一个新手机的类,对来电显示这个方法重写就好了,这样就达到了我们当今的需求了

【重写和重载的区别】

简而言之,方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。

1.4 向上转型

向上转型是发生多态的条件之一,其实际就是创建一个子类对象,将其当成父类对象来使用,简单来说,就是父类引用引用了子类对象。向上转型可以让代码的实现更简单灵活,但无法调用到子类特有的方法。

语法格式:

父类类型 对象名 = new 子类类型()

例如,小鸟是动物,就可以向上转型:

Animal animal = new Brid("小蓝",2);

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

向上转型的方法有三种:

1、直接赋值

    //两种直接赋值写法
    Dog dog = new Dog("毛球",3,"金色");
    Animal animal = dog;

    Animal animal1 = new Bird("小蓝",5);

2、方法传参

如下方代码中,fun() 方法中的参数 animal 可以接收 dog,也可以接收 bird。

    public static void fun(Animal animal) {}

    public static void main(String[] args) {
        Dog dog = new Dog("毛球",3,"金色");
        fun(dog);

        Bird bird = new Bird("小蓝",5);
        fun(bird);
    }

3、方法返回

如下方代码中,Animal 类是父类,Bird 类是子类,该方法是父类类型接收,返回任何一个子类类型都可以。

    public static Animal fun() {
        return new Bird("小蓝",5);
    }

1.5 向下转型

由于向上转型无法调用到子类特有的方法,所以 Java 中就引入了向下转型,把父类类型给到子类类型。

例如:由于父类 Animal 类无法直接调用子类 Bird 类特有的 fly() 方法,所以需要向下转型来调用fly() 方法。对 animal 进行强制类型转换为 Bird 类,然后再调用 fly() 方法,即可调用成功。

 

那我们是否可以尝试让其他动物对象也成功调用 Bird 类中的 fly() 方法,比如让小狗飞起来?

显然不行,报错说 Dog 无法转换为 Bird ,那是因为 animal 引用的本身就是 Bird,所以可以成功转换,但是 animal1 引用的是 Dog,所以无法转换,小狗还是飞不起来。

由此我们可以看出,向下转型还是不太安全,所以我们得在向下转型前加上一个判断,保证向下转型的安全性。利用 instanceof 关键字来判断引用的是否是正确的,如下图,animal1 引用的是 Dog,所以不会进行向下转型。

注:小鸟和小狗都是动物,但不是所有的动物都是小鸟,所以不是所有的动物都会飞,故向下转型时需要利用 instanceof 关键字来判断是否可以转型。

1.6 静态与动态绑定

静态绑定:也称为前期绑定 (早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用那个方法。例如,方法的重载就是静态绑定

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

如下方代码中,父类 Animal 类和子类 Dog 类都有 eat() 这个方法,发生了方法的重写,但是通过 animal 引用去调用 eat() ,编译时还是调用了父类中的 eat(),运行的时候就绑定到子类当中,最后运行输出子类 Dog 类里的 eat(),这个过程就是动态绑定。

class Animal {

    public void eat() {
        System.out.println("正在吃饭");
    }
}

class Dog extends Animal {

    @Override
    public void eat() {
        System.out.println("正在吃狗粮");
    }
}

public class Main {

    public static void main(String[] args) {
        //向上转型
        Animal animal = new Dog();
        animal.eat();//编译时输出正在吃饭,运行时输出正在吃狗粮
    }

}

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

先看个例子,父类 B 和子类 D 中都有 func() 方法,那父类的构造方法中调用 func() 方法,那子类利用 super() 调用父类构造方法时,父类构造方法中调用的 func() 方法是父类自己的还是子类的呢?

class B {

    public B() {
        func();
    }
    public void func() {
        System.out.println("B.func()");
    }
}

class D extends B {

    public D() {
        super();
    }
    @Override
    public void func() {
        System.out.println("D.func()");
    }
}

public class Test {
    public static void main(String[] args) {
        D d = new D();
    }
}

运行后发现,输出的是 D.func() ,即调用的是子类的 func() 方法。说明:当父类的构造方法中调用父类和子类同名的方法的时候,此时会发生动态绑定,意味着构造方法内也会发生动态绑定。

注:在构造方法当中要避免调用重写的方法。


总结

 1、返回值类型 方法名 (参数列表) 要完全一致,才能构成重写。

2、父类被static、private、final修饰的方法、构造方法都不能被重写。

3、子类重写父类方法时,子类的方法访问限定修饰符得 >= 父类。

4、向上转型无法调用到子类特有的方法。

5、向下转型时要用instanceof关键字来判断是否可以转型。

6、在构造方法当中要避免调用重写的方法。 

  • 7
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值