Java学习笔记

目录

前言

一、多态的引入

二、多态是什么

三、语法规则

四、向上转型

向上转型发生的时机

五、向下转型

instanceof关键字

 六、动态绑定

七、方法重写

关于重写的注意事项

八、总结

前言

根据前面继承的学习,我们以及了解到了继承是指子类继承父类的字段和方法,也知道了子类和父类的关系是(is ->a)的关系,在平时生活中我们就有猫是一只动物的说法,那么是不是在Java语言中也存在这么一种表现形式呢

一、多态的引入

好比之前继承的例子,设计一个类来表示动物

//Animal类
class Animal {
    //定义动物的名字
    protected String name;

    //定义动物的有参构造方法
    public Animal(String name) {
        this.name = name;
    }

    //定义动物的无参构造方法
    public Animal() {
    }

    //定义动物吃食物的方法
    public void eat(String food) {
        System.out.println(this.name + "正在吃" + food);
    }
}
//Cat类
class Cat extends Animal{
    //定义猫的名字
    protected String name;

    public Cat() {
    }

    public Cat(String name) {
        this.name = name;
    }

    //定义猫吃食物的方法
    public void eat(String food) {
        System.out.println(this.name + "正在吃" + food);
    }

    //定义猫跑的方法
    public void run() {
        System.out.println(this.name + "正在跑");
    }
}

//Bird类
class Bird extends Animal {
    public Bird() {
    }

    public Bird(String name) {
        this.name = name;
    }

    //定义鸟飞的方法
    public void fly() {
        System.out.println(this.name + "正在飞");
    }
}

按照我们之前类和对象的学习,创建一个对象的基本语法为

类名 变量名 = new 类名对应的构造方法;

Animal animal = new Animal ("小动物");

那么我们是不是也可以让父类对象,指向子类实例,就比如:

Animal animal = new Cat("喵喵");

上面这种现象在java语言中就被称之为多态

二、多态是什么

多态:将子类对象赋值给父类变量,即父类引用指向的是一个子类的实例(即子类对象)

就好比在形容一只猫你可以说它是一只动物一样

三、语法规则

基本语法

父类类名  变量名  = new  子类构造方法;

Animal     animal   = new   Cat("喵喵");

四、向上转型

 上面的例子中我们出现了形如Animal     animal   = new   Cat("喵喵");的代码,它实则是发生了向上转型,我们可以通过is -> a语义来进行理解;

我们看到喵喵在吃猫粮,我就可以说喵喵在吃猫粮,当然我也可以说这里有只动物在吃猫粮,因为喵喵是一只猫,但它同时也是一只动物;

向上转型发生的时机

直接赋值

方法传参

方法返回

直接赋值的方式我们在上面已经演示了. 另外两种方式其实和直接赋值没有本质区别.

方法传参

 此时形参animal 的类型是Animal (父类),实际上对应到Cat (子类) 的实例

方法返回

 此时方法findMyAnimal 返回的是一个 Animal 类型的引用,但是实际上对应的是 Bird 的实例

这种情况类似于:

double num = 100; // 正确,int --> double,自动类型转换

五、向下转型

既然向上转型是子类对象转换为父类对象,所以向下转型就是父类对象转换成子类对象,相比于向上转型来说,向下转型就没有那么常见了,但是也是有它一定的用途的

接下来就是我们熟悉的例子了

 按之前的讲解我们知道,这种现象是多态,既然咕咕是一只鸟,那么我们要是想让咕咕飞起来会发生什么呢?

 这时我们发现,报错了,那么这是为什么呢?

注意事项

编译过程中, animal 的类型是 Animal, 此时编译器只知道这个类中有一个 eat 方法, 没有 fly 方法.

虽然 animal 实际引用的是一个 Bird 对象, 但是编译器是以 animal 的类型来查看有哪些方法的.

对于 Animal animal = new Bird("圆圆") 这样的代码,

编译器检查有哪些方法存在, 看的是 Animal 这个类型

执行时究竟执行父类的方法还是子类的方法, 看的是 Bird 这个类型.

所以我们要是想实现刚才的效果, 就需要向下转型.

虽然这种方法可以让父类转换为子类对象,但是这样的向下转型有时是不可靠的,就比如

 诶,当我们将一只猫强制转换为鸟的时候就出现了ClassCastException异常,这个异常就是我们常常所说的类型转换异常,它指的是两种不可转换的类型之间发生了强制转换,那么这是为什么呢

animal 本质上引用的是一个Cat 对象,而Cat 对象是不能转换为 Bird 对象的,所以运行时就会抛出异常,那么我们有没有什么方法可以来判断转换是否能成功呢,所以我们引入了instanceof关键字

instanceof关键字

 instanceof关键字是用于判定一个引用是否为某个类的实例,如果是那么将返回 true ,如果不是那么就返回 false 所以经过判断后在进行向下转型就比较安全了,所以之前的代码我们可以改成

这样的话我们就可以保证向下转型的正确性

 六、动态绑定

之前的例子中我们子类和父类不存在同名方法,那么要是当子类和父类中出现同名方法的时候, 再去调用会出现什么情况呢?

对前面的代码稍加修改, 给 Bird 类也加上同名的 eat 方法, 并且在两个 eat 中分别加上不同的标志.

//Animal类
class Animal {
    //定义动物的名字
    protected String name;

    //定义动物的有参构造方法
    public Animal(String name) {
        this.name = name;
    }

    //定义动物的无参构造方法
    public Animal() {
    }

    //定义动物吃食物的方法
    public void eat(String food) {
        System.out.println("我是一只小动物");
        System.out.println(this.name + "正在吃" + food);
    }
}

//Bird类
class Bird extends Animal {
    public Bird() {
    }

    public Bird(String name) {
        this.name = name;
    }

    //定义鸟吃食物的方法
    public void eat(String food) {
        System.out.println("我是一只小鸟");
        System.out.println(this.name + "正在吃" + food);
    }
    
    //定义鸟飞的方法
    public void fly() {
        System.out.println(this.name + "正在飞");
    }
}

此时,我们发现这样一个现象:

        animalA 和animalB 虽然都是Animal 类型的引用,但是 animalA 指向了Animal 类型的实例,而 animalB 指向了 Bird 类型的实例

        针对 animal1 和 animal2 分别调用 eat 方法, 发现 animal1.eat() 实际调用了父类的方法, 而animal2.eat() 实际调用了子类的方法. 

        因此, 在 Java 中, 调用某个类的方法, 究竟执行了哪段代码 (是父类方法的代码还是子类方法的代码) , 要看究竟这个引用指向的是父类对象还是子类对象. 这个过程是程序运行时决定的(而不是编译期), 因此称为 动态绑定.

七、方法重写

针对刚刚动态绑定中的 eat 方法来说,子类实现了父类的同名方法,并且参数的类型和个数与父类完全相同,这种情况我们就把它称之为覆写/重写/覆盖(Override).

关于重写的注意事项

 

1. 重写和重载完全不一样. 不要混淆(可以思考一下, 重载的规则又是啥?)

2. 普通方法可以重写, static 修饰的静态方法不能重写.

3. 重写中子类的方法的访问权限不能低于父类的方法访问权限.

4. 重写的方法返回值类型不一定和父类的方法相同(但是建议最好写成相同, 特殊情况除外).

方法权限示例: 将子类的 eat 改成 private

 另外,针对重写的方法,我们可以使用@Override 注解来显式指定.

 

有了这个注解能帮我们进行一些合法性校验. 例如不小心将方法名字拼写错了 (比如写成 aet), 那么此时编译器就会发现父类中没有 aet 方法, 就会编译报错, 提示无法构成重写.

所以一般我们推荐在代码中进行重写方法时显式加上 @Override 注解.

八、总结

       

 

小结: 重载和重写的区别.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值