多态的左膀右臂,向上转型与重写

本文详细讲解了Java中的多态、向上转型、向下转型、重写以及它们之间的关系,包括向上转型的优点与缺点、重写规则、动态与静态绑定,以及如何在实际编程中利用这些概念实现多态。
摘要由CSDN通过智能技术生成

同一个引用 调用了 同一个方法,但是因为引用的对象不一样,所表现的行为不一样,我们把这种思想称为:多态

目录

1.向上转型

1.1.向上转型定义

1.2.向上转型的优缺点

1.3.向上转型的内存指向

1.4.向上转型的三种方式

1.5.向下转型

2.重写

2.1.概念

2.2.重写的规则

2.3.重写与重载

2.4.动态与静态绑定

2.5.避免在构造方法中调用重写的方法

3.使用多态

3.1.简单的多态实现

3.2.实现自定义多态


学会多态的前提:

(1)继承(2)向上转型(3)重写

继承在之前的课程已经介绍过,下面介绍向上转型和重写,这两种也是发生在继承的基础上。

1.向上转型

1.1.向上转型定义

概念:父类引用 引用 子类对象

语法格式:

父类类型 引用名 = new 子类类型();

例如:Parent是父类类型,Son是子类类型,此时就发生了向上转型

Parent p = new Son();
1.2.向上转型的优缺点

优点:

(1)可以实现代码的灵活使用 (2)实现多态的一种条件

缺点:

(1)虽然引用了子类对象,但是也无法访问子类对象中特有的内容

这是一个父子类:

public 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 String color;

    public Dog(String name, int age,String color) {
        super(name, age);
        this.color = color;
    }
    public void sleep() {
        System.out.println(name+"正在睡觉!");
    }
}

当访问的时候: 父类引用无法访问子类单独的方法

(2)但是可以访问父类子类都存在的方法(重写),这种是发生了动态绑定,在下面的重写部分介绍

1.3.向上转型的内存指向

向上转型就相当于,把子类对象当成父类使用。如果需要调用子类独有的属性,需要通过向下转型回来才能使用。

 

1.4.向上转型的三种方式

父类、子类

public 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 String color;
    public Dog(String name, int age,String color) {
        super(name, age);
        this.color = color;
    }
    public void eat() {
        System.out.println("正在吃史");
    }
    public void sleep() {
        System.out.println(name+"正在睡觉!");
    }
}

1.4.1.直接赋值

第一种:

 public static void main(String[] args) {
        Dog dog = new Dog("旺财",2,"黄色");
        Animal animal = dog;//向上转型
        animal.eat();
    }

第二种:

public static void main(String[] args) {
        Animal animal = new Dog("旺财",2,"黄色");
        animal.eat();
    }

1.4.2传参方式

public static void func(Animal animal) {
        animal.eat();
        animal.age = 3;
    }
    public static void main(String[] args) {
        Dog dog = new Dog("旺旺",1,"黑色");
        func(dog);
    }

1.4.3.通过返回值

public static Animal func2() {
        return  new Dog("旺旺",1,"黑色");
    }
    public static void main(String[] args) {
        Animal animal = func2();
        animal.name  ="666";
    }

介绍了向上转型,在下面介绍向下转型。

1.5.向下转型

向下转型一般是使用在向上转型之后,因父类引用无法访问子类特有的方法

转型方式:

  public static void main(String[] args) {
        Animal animal = new Dog("1",1,"1");
        animal.eat();
        Dog dog = (Dog)animal;
        dog.sleep();
        ((Dog) animal).sleep();
    }

注意事项:

总结:

向上转型是安全的,而向下转型是不安全的,慎用。比如:狗一定是动物,但是动物不一定是狗。

做法:经历对实现了向上转型的对象,再使用向下转型

2.重写

重写发生的条件:发生继承

2.1.概念

2.1.1.命名

重写,又称为覆写,覆盖。

2.1.2.啥是重写

对父类的方法进行重新编写

例如:

 

2.2.重写的规则

重写的模板:

(1)返回值、形参都不能被修改,必须相同。(返回值如果构成父子类关系也可以)

(2)子类重写的方法的修饰访问权限的大小 >= 父类的

不能被重写的:

(1)被final修饰的方法

(2)静态方法

(3)构造方法

(4)被private修饰的方法

以上父类中的方法都是不能被子类重写的!

注解:

(1)重写的注解:@Override

(2)作用:可以标识是被重写的方法;可以帮助校验重写的格式、语法是否正确,否则会报错。

2.3.重写与重载
区别点重写(Override)重载(Overloading)
参数列表一定不能修改必须修改
返回值类一定不能修改(除非构造父子类关系)可以修改
访问限定符一定不能做到更严格的限制(可以降低限制)可以修改
方法重载是一个类的多态性表现 , 而方法重写是子类与父类的一种多态性表现。

2.4.动态与静态绑定

2.4.1.静态绑定

也称为前期绑定,这种代表就是方法重载,在编译时候(代码写完的时候),根据用户传递的参数就确定了 会调用的方法。

2.4.2.动态绑定

也称为后期绑定,典型代表就是重写。在编译期间,是不确定会调用哪一个方法的,只有在程序运行时,才能确定调用的方法。

2.5.避免在构造方法中调用重写的方法

(1)第一种情况

class B {
    public B() {
        func();//在构造方法中,调用重写方法(子类父类都有)
    }
    public void func() {
        System.out.println("B.func()");
    }
}
class D extends B {
    @Override
    public void func() {
        System.out.println("D.func() ");
    }
}
public class Test2 {
    public static void main(String[] args) {
        D d = new D();
    }
}

在父类的构造方法中,调用了重写的方法。在实例化子类对象的时候,调用的是子类的func()方法。说明在构造方法内,也会发生动态绑定。

(2)第二种情况

class B {
    public B() {
        func();//在构造方法中,调用重写方法(子类父类都有)
    }
    public void func() {
        System.out.println("B.func()");
    }
}
class D extends B {
    private int num = 1;
    @Override
    public void func() {
        System.out.println("D.func() "+num);
    }
}
public class Test2 {
    public static void main(String[] args) {
        D d = new D();
    }
}

实例化子类对象时,为什么不会给num赋值

代码指向顺序:实例化子类对象-->子类构造-->父类的构造方法-->调用重写方法,此时num没有被赋值。(当实例化子类的时候,会给子类对象分配一个内存空间,此时属性都会被加载出来,但是没有进行初始化,就默认为0)

说明:

用尽量简单的方式使对象进入可工作状态 ", 尽量不要在构造器中调用方法 ( 如果这个方法被子类重写 , 就会触
发动态绑定 , 但是此时子类对象还没构造完成 ), 可能会出现一些隐藏的但是又极难发现的问题 .

3.使用多态

在上面介绍向上转型的时候,就已经接触到一点多态的影子了。

一个父类、两个子类

public 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);
    }
    @Override
    public void eat() {
        System.out.println("正在吃狗粮……");
    }
}
class Cat extends Animal {
    public Cat(String name, int age) {
        super(name, age);
    }
    @Override
    public void eat() {
        System.out.println("正在吃鱼翅……");
    }
}
3.1.简单的多态实现

3.1.1.先发生重写

3.1.2.发生向上转型

我们使用传参的方式进行向上转型

  public static void func(Animal animal) {
        animal.eat();
    }
    public static void main(String[] args) {
        Dog dog = new Dog("小狗",1);
        Cat cat = new Cat("小猫",2);
        func(dog);
        func(cat);
    }

3.1.3.多态的发生

(1)多态发生

(2)多态的概念

同一个引用 调用了 同一个方法,但是因为引用的对象不一样,所表现的行为不一样,我们把这种思想称为:多态

3.2.实现自定义多态

下面这种实现的方向,才是以后多态实现的常用手段。一般是通过返回值实现多态

类:

public 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);
    }
    @Override
    public void eat() {
        System.out.println(name+"正在吃狗粮……");
    }
}
class Cat extends Animal {
    public Cat(String name, int age) {
        super(name, age);
    }
    @Override
    public void eat() {
        System.out.println(name+"正在吃鱼翅……");
    }
}

main函数:

import java.util.Scanner;

public class Test {
    public static void func1(Animal animal) {
        animal.eat();
    }
    public static Animal func(int q) {
        Scanner in = new Scanner(System.in);
        if(q == 1) {
            System.out.print("输入名字:");
            String s = in.nextLine();
            System.out.print("输入年龄:");
            int b = in.nextInt();
            return new Dog(s,b);
        }else {
            System.out.print("输入名字:");
            String s = in.nextLine();
            System.out.print("输入年龄:");
            int b = in.nextInt();
            return new Cat(s,b);
        }
    }
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);

        int q = 0;
        do {
            System.out.println("请输入你的身份:1(dog)/2(cat)");
            q = in.nextInt();
            Animal animal = func(q);
            animal.eat();
        }while (q == 1 || q == 2);
        
    }
}

运行结果:


本节的多态知识就介绍到这里了,快去按照代码练习一下吧。

 

  • 28
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

代码小娥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值