java面向对象进阶篇--《多态》

目录

一、前言

二、认识多态

方法重写(Override):

方法重载(Overload):

示例:

Person类(父类)

 Administrator(子类)

Student(子类)

Teacher(子类)

Text(测试类)

运行结果

示例解释

类的定义:

对象的创建和初始化:

方法调用:

多态的体现:

三、多态中调用成员的特点

动态绑定:

方法的重写(Override):

成员变量的访问:

静态方法和私有方法:

向上转型:

运行时类型识别(Instanceof):

示例:

类的结构:

对象的创建:

成员变量访问:

方法的调用:

总结:

四、多态的优势和弊端

优势:

灵活性和扩展性:

简化代码:

可替换性:

接口和抽象类的应用:

代码可读性和可理解性:

弊端:

运行时性能开销:

隐藏对象的实际类型:

可能引发运行时异常:

复杂性增加:

解决方法:

完整代码:

五、多态的综合练习

案例:饲养员喂动物

结果展示:

Animal类

Cat类

Dog类

Person类


一、前言

继续我们面向对象篇的学习,今天学习的是继承,这部分特别容易迷,多态是重点也是难点部分。概念了解一下即可,关键的会使用它。如果想要快速了解多态的用法,就直接看第五章。第五章是一个综合案例的全套源代码。

如果看着有些困难,不妨去看一下博主的另一篇文章。

 java继承icon-default.png?t=N7T8http://t.csdnimg.cn/2ZyyD

二、认识多态

多态(Polymorphism)是面向对象编程中一个重要的概念,它允许在不同类的对象上执行相同的操作,但具体执行的操作可能会有所不同。在Java中,多态性通过方法重写(Override)和方法重载(Overload)实现。

方法重写(Override)

  • 方法重写指子类重新定义或实现其父类的方法,方法名、参数列表和返回类型必须与父类方法完全一致。
  • 当子类对象通过父类引用调用被重写的方法时,实际执行的是子类的方法,而不是父类的方法。
  • 这种机制允许不同的子类对象表现出各自的行为,而不需要修改调用这些方法的代码。

方法重载(Overload)

  • 方法重载指在同一个类中,可以定义多个方法名相同但参数列表不同(参数类型、个数或顺序)的方法。
  • 编译器根据方法调用时传入的参数类型来决定具体调用哪个方法。
  • 方法重载并不是多态的实现方式,但它提供了一种静态的多态,即在编译阶段就确定了调用的方法。

示例:

Person类(父类)

public class Person {
    private String name;
    private int age;

    public Person() {
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void show(){
        System.out.println(name+","+age);
    }
}

 Administrator(子类)

public class Administrator extends Person{
    @Override
    public void show() {
        System.out.println("管理员的信息为:"+getName()+getAge());
    }
}

Student(子类)

public class Student extends Person{
    @Override
    public void show() {
        System.out.println("学生的信息为:"+getName()+getAge());
    }
}

Teacher(子类)

public class Teacher extends Person{
    @Override
    public void show() {
        System.out.println("老师的信息为:"+getName()+getAge());
    }
}

Text(测试类)

public class Text {
    public static void main(String[] args) {
        Student s=new Student();
        s.setName("王同学");
        s.setAge(18);

        Teacher t=new Teacher();
        t.setName("王老师");
        t.setAge(30);

        Administrator a=new Administrator();
        a.setName("王管理员");
        a.setAge(40);

        register(s);
        register(t);
        register(a);
    }
    public static void register(Person p){
        p.show();
    }
}

运行结果

示例解释

类的定义

  • Text 类是程序的入口,其中包含了 main 方法。
  • StudentTeacherAdministrator 类都是 Person 类的子类,它们分别表示学生、老师和管理员,都继承自 Person 类。

对象的创建和初始化

  • Student s = new Student(); 创建了一个 Student 类型的对象 s,并设置了其姓名和年龄。
  • Teacher t = new Teacher(); 创建了一个 Teacher 类型的对象 t,并设置了其姓名和年龄。
  • Administrator a = new Administrator(); 创建了一个 Administrator 类型的对象 a,并设置了其姓名和年龄。

方法调用

  • register(s);register(t);register(a); 分别调用了 register 方法,并传入不同类型的对象作为参数。
  • register 方法的参数类型为 Person,因此可以接受 StudentTeacherAdministrator 类型的对象,这展示了多态的应用。
  • 在 register 方法内部,调用了 Person 类的 show 方法。由于子类 StudentTeacherAdministrator 都重写了 Person 类的 show 方法,因此实际上调用的是各自子类的 show 方法。

多态的体现

  • 多态使得在 register 方法中可以使用 Person 类型的参数,而实际传入的可以是其任何子类的对象。在运行时,根据实际对象的类型确定调用哪个子类的方法,这样可以根据需要处理不同类型的对象,而无需改变方法的定义。

三、多态中调用成员的特点

在Java中,多态性主要通过方法的动态绑定(也称为运行时多态)来实现。这种特性使得在运行时根据对象的实际类型来确定调用的方法,而不是在编译时确定。

动态绑定

  • 当通过父类引用调用子类对象的方法时,实际执行的是子类重写(覆盖)的方法。例如,如果父类有一个方法,子类覆盖了这个方法,那么当通过父类类型的引用调用这个方法时,实际执行的是子类的版本。

方法的重写(Override)

  • 子类可以通过重写父类的方法来提供特定于子类的实现。当父类的方法在子类中被重写后,通过父类引用调用该方法时,会调用到子类的方法实现。

成员变量的访问

  • 多态情况下,成员变量不具备多态性。即使子类覆盖了父类的成员变量,通过父类引用访问的仍然是父类的成员变量。这是因为成员变量的访问在编译时就已经确定了,而不会等到运行时才决定。

静态方法和私有方法

  • 静态方法和私有方法不具备多态性。静态方法是在编译时就确定的,因此不会被动态绑定,而是根据引用类型来确定调用的方法。私有方法也类似,子类中的同名私有方法不会覆盖父类的私有方法。

向上转型

  • 将子类实例赋给父类引用,称为向上转型。通过向上转型,可以实现对不同子类对象的统一处理,提高代码的灵活性和可扩展性。

运行时类型识别(Instanceof)

  • 可以使用 instanceof 运算符来判断一个对象的实际类型,这在多态中尤为有用,可以根据需要进行类型检查和转型操作。

示例:

public class text1 {
    public static void main(String[] args) {
        Fu f=new adog();
        Fu f1=new bdog();
        //调用成员变量
        System.out.println(f.name);
        System.out.println(f1.name);
        //调用方法
        f.show();
        f1.show();
    }

}
class Fu{
    String name="Fu";
    public void show()
    {
        System.out.println("Fu--show");
    }
}
class adog extends Fu{
    String name="adog";
    @Override
    public void show()
    {
        System.out.println("adog--show");
    }
}
class bdog extends Fu{
    String name="bdog";
    @Override
    public void show()
    {
        System.out.println("bdog--show");
    }
}
类的结构
  • text1 类是程序的入口,包含了 main 方法。在 main 方法中,创建了两个 Fu 类型的引用,分别指向 adog 和 bdog 对象。
对象的创建
  • Fu f = new adog(); 创建了一个 adog 对象,并用 Fu 类型的引用 f 指向它。这是向上转型的示例,父类引用指向子类对象。
  • Fu f1 = new bdog(); 创建了一个 bdog 对象,并用 Fu 类型的引用 f1 指向它。
成员变量访问
  • 在Java中,成员变量不具备多态性。无论引用是什么类型,成员变量的访问都是由引用的类型决定的,而不是对象的实际类型。
  • Fu 类中有一个 name 成员变量,初始化为 "Fu"
  • adog 类和 bdog 类分别有自己的 name 成员变量,但是它们并不会覆盖父类 Fu 的 name 成员变量,而是在各自的类中定义了新的 name 变量。
方法的调用
  • 在多态的情况下,调用的方法由对象的实际类型决定。
  • f.show(); 调用的是 adog 类中重写的 show 方法,输出 "adog--show"
  • f1.show(); 调用的是 bdog 类中重写的 show 方法,输出 "bdog--show"
总结
  • 在成员变量访问中,引用类型决定了访问的成员变量,而方法调用则是根据对象的实际类型决定的(多态)。
  • 成员变量在多态中表现为静态绑定,而方法在多态中表现为动态绑定(运行时绑定)。
  • 成员变量看父类自己的,成员方法看是new的哪个对象。

四、多态的优势和弊端

优势:

灵活性和扩展性

  • 多态允许使用父类的引用来引用子类的对象,这样可以统一处理不同子类对象,提高了代码的灵活性和可扩展性。通过添加新的子类,而不需要修改现有的代码,可以轻松地扩展程序的功能。

简化代码

  • 多态可以使代码更加简洁,通过统一的接口来操作不同对象,减少了重复的代码,提高了代码的可读性和维护性。

可替换性

  • 可以随时替换具体的子类对象,而无需修改调用这些对象的代码。这种可替换性使得在运行时动态地改变程序的行为成为可能。

接口和抽象类的应用

  • 多态性使得接口和抽象类能够更好地发挥作用。通过接口和抽象类定义统一的规范,具体的子类可以按照自己的方式实现这些规范,从而提高了代码的可复用性和可维护性。

代码可读性和可理解性

  • 使用多态性可以使代码更加清晰和易于理解。通过使用父类类型的引用来操作对象,可以更专注于对象的行为和功能,而不必关注对象的具体类型。

弊端:

运行时性能开销

  • 在运行时,系统需要动态地确定对象的类型,然后调用相应的方法。这种动态绑定的过程可能会导致一些性能开销,尤其是在需要频繁调用的地方。

隐藏对象的实际类型

  • 多态性有时会隐藏对象的实际类型,这可能导致代码阅读和调试时的困惑。特别是当对象的具体类型在运行时才能确定时,理解代码的行为可能会更具挑战性。

可能引发运行时异常

  • 如果没有正确处理多态的边界条件和对象状态,可能会导致运行时异常,如空指针异常或类型转换异常。这需要在设计和实现中格外注意。

复杂性增加

  • 如果过度使用多态,可能会导致代码结构变得复杂,不易于理解和维护。因此,需要在设计中平衡使用多态性和明确的类型设计。

以上述代码为例,在子类中添加了一个特有的方法,因为父类中不存在,所以无法调用。 

解决方法:

完整代码:

//解决方法,将f强转为adog
        if(f instanceof adog a){
            a.adogshow();
        }else if(f instanceof bdog b){
            b.bdogshow();
        }else{
            System.out.println("没有这个类型,无法转换");
        }

instanceof关键字 

public void bdogshow()
    {
        System.out.println("bdog--bdogshow");
    }

为了代码的可读性,又在bdog类中添加了bdogshow方法。

注:不能转换成其他类型,容易报错。

五、多态的综合练习

写多态综合练习的时候,要将每一个类进行单独打包,Text测试类中不要有类的定义。

案例:饲养员喂动物

结果展示:

Animal类

package com.von.day11.text7;

public class Animal {
    private int age;
    private String color;

    public Animal() {
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    //共有方法
    public void eat(String something)
    {
        System.out.println("动物吃"+something);
    }
}

Cat类

package com.von.day11.text7;

public class Cat extends Animal{
    public Cat() {
    }

    public Cat(int age, String color) {
        super(age, color);
    }

    @Override
    public void eat(String something) {
        System.out.println(getAge()+"岁的"+getColor()+"颜色的猫吃"+something);
    }

    public void catchMouse() {
        System.out.println("猫抓老鼠");
    }
}

Dog类

package com.von.day11.text7;

public class Dog extends Animal{

    public Dog() {
    }

    public Dog(int age, String color) {
        super(age, color);
    }

    @Override
    public void eat(String something) {
        System.out.println(getAge()+"岁的"+getColor()+"颜色的狗抱住"+something+"猛吃");
    }

    public void lookHome(){
        System.out.println("狗看家");
    }
}

Person类

package com.von.day11.text7;

public class Person {
    private String name;
    private int age;

    public Person() {
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    //饲养狗
    public void keepPet(Dog dog, String something)
    {
        System.out.println("年龄为" + this.age + "岁的" + this.name + "养了一只" + dog.getColor() + "颜色的" + dog.getAge() + "岁的狗");
        dog.eat(something);
    }
    //饲养猫
    public void keepPet(Cat cat, String something)
    {
        System.out.println("年龄为" + this.age + "岁的" + this.name + "养了一只" + cat.getColor() + "颜色的" + cat.getAge() + "岁的猫");
        cat.eat(something);
    }
}

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值