栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java入门——多态篇,6k字攻略。

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java入门——多态篇,6k字攻略。

目录

多态性

向上转型

方法重写

问题:父类方法使用 private ,子类方法使用 public 是否可以?

问题:能否重写 static 方法?

判断父类是否被覆写、子类是否重写父类方法

方法重写的返回值必须严格相同,向上转型类除外

向上转型发生的时机

向下转型

要发生向下转型,首先要发生向上转型

instanceof 关键字


多态性

多态:同一个引用可以表现出多种行为或特性。

最主要的多态的表现形式:继承+方法重写。

例如:都是 Animal 类的子类,都有 eat 方法,不同子类对象调用 eat 方法表现出来的行为就不同(调用的是相应子类覆写后的 eat 方法)。

向上转型

向上转型:发生在有继承关系的类之间。最大的意义在于参数统一化(使用一个共同父类引用来接受所有子类对象),降低使用者的使用难度。


常见的创建对象:

类名称 类引用= new 该类对象();

子类 is a 子类——>语义:Dog is a dog

Dog dog = new Dog();//最常见的

向上转型的创建对象:

父类名称 父类引用= new 子类对象(); //此处 new 的不一定是直接子类,也可以是孙子类。。子类天然 is a 父类——>天然语义:Dog is an Animal

Animal animal = new Dog();//向上转型

 假设没有向上转型

package polymorphism;
public class Test {
    public static void main(String[] args) {
        // 2.作为类的使用者,程序的使用者
        // 没有向上转型,我要使用fun方法,就得了解Animal以及其子类的所有对象
        // 我才能知道调用的是谁
        Animal animal1 = new Animal();
        Animal animal2 = new Bird();
        Animal animal3 = new Duck();
        fun(animal1);
        fun(animal2);
        fun(animal3);

    }
    //1.作为类的实现者
    // fun方法接收AnimaL以及其子类的对象作为参数
    // 假设现在没有向上转型,AnimaL有多少子类,我就得重载多少次fun方法
    // 大自然 Animal 动物子类有几百万种,就有几百万种子类,fun 就得写上百万次!
    public static void fun(Animal animal) {}
    public static void fun(Bird bird) {}
    public static void fun(Duck duck) {}
}

既然子类是天然的父类(Dog is an Animal),为何不能用父类去指代所有的子类,向上转型因此就产生了。


有向上转型后的代码

只要是 Animal 及其子类,都是天然的 Animal 对象,都满足is a关系。
最顶层的父类 Animal 引用,指代所有的子类对象。此时 Animal 是非常容易扩展一个新的子类的。

//Animal.java   Animal类单独的java文件
public class Animal {
    public void eat() {
        System.out.println("Animal类的eat方法");
    }
}
//Bird.java   Bird类单独的java文件
public class Bird extends Animal{
    public void eat() {
        System.out.println("Bird类的eat方法");
    }
}
//Duck.java   Duck类单独的java文件
public class Duck extends Bird{
    public void eat() {
        System.out.println("Duck类的eat方法");
    }
}
//Dog.java   Dog类单独的java文件
public class Dog extends Animal{
    public void eat() {
        System.out.println("Dog类的eat方法");
    }
}
//Test.java   Test类单独的java文件
package polymorphism;
public class Test {
    public static void main(String[] args) {
        Animal animal1 = new Animal();
        Animal animal2 = new Bird();
        Animal animal3 = new Duck();
        Animal animal4 = new Dog();    //新增 Dog 类
        fun(animal1);
        fun(animal2);
        fun(animal3);
        fun(animal4);
    }
    // 只要是Animal及其子类,都是天然的Animal对象,都满足is a关系
    // 通过AnimaL最顶层的父类引用,指代所有的子类对象。
    public static void fun(Animal animal) {
        animal.eat();
    }
}

fun 中 animal 局部变量的引用调用 eat 方法时,当传入不同的对象时,表现出来了不同的 eat 方法行为叫做多态性。

多态性:同一个引用(变量名称),同一个方法名称根据对象的不同表现出来了不同的行为。

方法重写

方法重载(overload):发生在同一个类中,定义了若干个方法名称相同,参数列表不同的一组方法。
方法重写(override):发生在有继承关系的类之间。子类定义了和父类除了权限不同,方法名称,返回值,参数列表完全相同(返回值完全相同或者至少是向上转型类的返回值),这样的一组方法称之为方法重写。

不用去看前半部分,看当前是通过哪个类 new 的对象,若该类重写了相关方法,则调用的一定是重写后的方法。
只要new的这个对象的类中覆写了同名方法,则调用的一定是覆写后的方法。


若子类没有重写方法,调用的是什么?

//Animal.java   Animal类单独的java文件
public class Animal {
    public void eat() {
        System.out.println("Animal类的eat方法");
    }
}

//Bird.java   Bird类单独的java文件
public class Bird extends Animal{
    public void eat() {
        System.out.println("Bird类的eat方法");
    }
}

//Duck.java   Duck类单独的java文件
public class Duck extends Bird{
        //没有重写eat方法
}

//Test.java   Test类单独的java文件
package polymorphism;
public class Test {
    public static void main(String[] args) {
        Animal animal1 = new Animal();
        Animal animal2 = new Bird();
        Animal animal3 = new Duck();
        fun(animal1);
        fun(animal2);
        fun(animal3);
    }

    public static void fun(Animal animal) {
        animal.eat();
    }
}

就近匹配规则,碰到最接近的调用。

当发生重写时,子类权限必须 >= 父类权限才可以重写。

private < default < protected < public。


当子类权限<父类权限时

//Animal.java   Animal类单独的java文件
public class Animal {
    public void eat() {
        System.out.println("Animal类的eat方法");
    }
}

//Bird.java   Bird类单独的java文件
public class Bird extends Animal{
    protected void eat() {
        System.out.println("Bird类的eat方法");
    }
}
//运行时报错


问题:父类方法使用 private ,子类方法使用 public 是否可以重写?

public > private

答: private 权限不包含在内

package combination;
class Person {
    public void fun() {
        this.test();    //表示此时在父类Person中
    }
    private void test() { //private 权限,子类根本不知道 test 方法,无法覆写
        System.out.println("1. Person的fun方法");
    }
}

class Student extends Person {
    public void test() {
        System.out.println("2. Student的fun方法");
    }
}

class Test {
    public static void main(String[] args) {
        new Student().fun(); //父类的 fun 方法
    }
}
//输出结果
1. Person的fun方法

Java中的注解@Override
使用这个注解写在重写方法之前,可校验方法重写是否符合规则。

此处显示方法不可重写。


问题:能否重写 static 方法?

答:多态的本质就是因为调用了不同的子类“对象”,这些子类对象所属的类,覆写相应的方法才能表现出不同的行为。但是static和对象无关,所以不能重写 static 方法。

//Animal.java   Animal类单独的java文件
public class Animal {
    public static void eat() {        //使用static修饰
        System.out.println("Animal类的eat方法");
    }
}

//Bird.java   Bird类单独的java文件
public class Bird extends Animal{
    protected void eat() {            //此处出现错误
        System.out.println("Bird类的eat方法");
    }
}
//运行时报错


判断父类是否被覆写、子类是否重写父类方法


方法重写的返回值必须严格相同,向上转型类除外

1. 返回值不相同的情况

 2. 返回值为向上转型类

运行成功

3. 返回值为向下转型

运行出错,不算方法重写

小结: 

NO区别重载(overload)覆写(override)
1概念

方法名称相同,

参数的类型及个数不同

返回值无关

方法名称、返回值类型、

参数的类型及个数
完全相同

(或返回值为向上转型类)

2范围一个类中继承关系的类
3限制没有权限要求

被覆写的方法不能拥有
比父类更严格的访问控
制权限(>=)

(不含private)

4

用于

static

没要求不能重写static方法
向上转型发生的时机

引用赋值、方法传参、方法返回值

 题目练习:求输出结果?

//B.java   B类单独的java文件
package polymorphism;
public class B {
    public B() {

    }

    public void fun() {
        System.out.println("B的fun方法");
    }
}
//D.java   D类单独的java文件
package polymorphism;
public class D extends B {
    private int num = 10;

    public void fun() {
        System.out.println("D的fun方法,num = " + num);
    }

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

答:

使用父类引用调用普通方法时,若子类重写了该方法,则调用该对象所在子类覆写后的方法。


题目:假设此时Dog类中有一个扩展方法play(),这个方法Animal类中不具备,此时还能通过 animal.play() 吗?

//Animal.java   Animal类单独的java文件
public class Animal {
    public void eat() {
        System.out.println("Animal类的eat方法");
    }
}
//Dog.java   Dog类单独的java文件
public class Dog extends Animal{
     public void eat() {
        System.out.println("Dog类的eat方法");
    }
    public void play() {
        System.out.println("Dog类独有的play方法");
    }
    public static void main(String[] args) {
        Animal animal = new Dog();
        animal.eat();
        animal.play();    //报错 Animal 类中没有 play 方法
    }
}

答:不能

类名称 引用名称 = new 类实例();
引用名称.方法名称();

能通过" . "访问的方法,类名称说了算。能访问的这些方法必须都在类中定义过,编译器会充在类中查找是否包含指定方法。

至于这个方法到底表现出来是哪个类的样子,实例所在的方法说了算。

Animal animal = new Dog();

animal.play();

animal 这个引用在本质上是个 Dog,批了个 Animal 的外衣,此时只能调用 Animal 中定义的方法。


若此时就想调用子类扩展的方法

脱掉这层 Animal 外衣,还原为子类引用——>向下转型

子类名称 子类引用 = (子类名称) 父类引用;

Dog dog =(Dog) animal; //脱掉 animal 对应的对象的外衣还原为具体的子类引用
向下转型

将父类引用强制类型转换为子类引用。
子类is a父类——>天然的 Dog is an animal

父类is a子类(不—定), Animal is a Dog?


通过向下转型使用子类独有方法

要发生向下转型,首先要发生向上转型

毫无关系的两个类之间没法强转(如 int boolean类型)

Animal animal = new Dog();    //本质是个 Dog 类对象(向上转型)
Dog dog = (Dog) animal;       //向下转型(强制类型转换)正确

Animal animal = new Animal(); //本质是个 Aniaml 类的对象,与 Dog 类无关
Dog dog = (Dog) animal;       //向下转型(强制类型转换)错误

 报错,类型转换异常。强制把两个毫无关系的类的引用进行强转。

instanceof 关键字

当发生向下转型时会有风险,类型转换异常,使用 instanceof 关键字。
引用名称 instanceof 类——>返回布尔值,表示该引用指向的本质是不是该类的对象 。


使用 instanceof 关键字的返回值搭配分支语句进行类型转换

instanceof 关键字的作用是判断左边对象是否是右边类的实例(子类对象或右边类本身的对象)返回的boolean类型,true 或 false。

小结

什么时候发生向上转型:

public class Test {
    public static void main(String[] args) {
        Animal animal = new Dog();//向上转型
        fun(animal);//父类引用animal
    }
    public static void fun(Animal animal) {
        animal.eat();
    }
}

方法接收一个类和当前类的子类,参数指定为相应的父类引用,发生的就是向上转型。

什么时候发生向下转型:
只有某些特殊的情况下,需要使用子类拓展的方法,才需要将原本向上转型的引用向下转型,还原为子类引用。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/855501.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号