Java继承和多态

  1. 继承的介绍与使用

继承:让类与类之间产生关系(子父类关系),子类可以直接使用父类中非私有的成员

场景:现在需要编写三个类,描述程序员,项目经理,人事,属性为(姓名,年龄,工资)

可以发现,三个类中的属性完全一样,重复编写就非常麻烦,这时我们可以抽取一个共同的父类 Employee,将共性内容定义在父类中,再让三个子类继承 Employee,复用性就提高了。

只继承属性,不影响父子类各自属性的值。

继承的是【属性定义】,不是【属性值】

  • 子类不用重复写String name;int age...

  • 子类可以直接setName,此时设置的是子类自己的,跟父类无关

  • 父类可以直接setName,此时设置的是父类自己的,跟子类无关

我只是有了我爸的房子,我、我爸在这个房子做什么,互不干扰

格式

public class Employee {
    String name;   // 姓名
    int age;       // 年龄
    double salary; // 工资
}
public class Coder extends Employee {

}

public class Manager extends Employee {

}

public class Hr extends Employee {

}
public class Test {
    public static void main (String[] args){
        Coder c = new Coder();
        c.name = "张三";
        c.age = 23;
        c.salary = 10000;
        
        Manager a = new Manager();
        a.name = "李四";
        a.age = 24;
        a.salary = 15000;
        
        Hr h = new Hr();
        h.name = "王五";
        h.age = 25;
        h.salary = 7000;
    }
}

可以发现父类中非私有的属性,子类是可以直接使用的。

问题:按照标准的 JavaBean 要求,成员变量是需要私有的,那子类怎么使用呢?

回答:私有成员变量后,我们还会提供对应的 setXxx 和 getXxx 方法,这些方法是 public 公共的,子类可以调用这些方法解决问题。

package com.itheima.mextends;

public class ExtendsDemo1 {
    public static void main(String[] args) {
        Coder c = new Coder();
        c.setName("张三");
        c.setAge(23);
        c.setSalary(12000);

        System.out.println(c.getName() + "---" + c.getAge() + "---" + c.getSalary());
    }
}

class Employee {

    private String name;
    private int age;
    private double salary;

    public void work() {
        System.out.println("员工工作");
    }

    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 double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }
}

class Coder extends Employee {

}

class Manager extends Employee {

}

使用场景

好处与弊端

  • 好处:提高了代码的复用性,维护性

  • 弊端:类和类之间的耦合性增强了

继承的学习路径

  1. 继承中成员变量的访问特点

思考:子父类中,如果出现了重名的成员变量,使用的时候会优先使用??

this:调用本类成员

super:调用父类成员

  1. 方法重写

    思考:子类继承了父类之后,如果编写的方法结构和父类相同,但是方法内逻辑不相同,创建子类对象,调用方法的时候,执行的是父类方法逻辑,还是子类方法逻辑?

答案:子类方法逻辑,但这不是就近原则,是子类的方法,对父类方法进行了重写。

介绍

在继承体系中,子类可以继承到父类的方法

但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改

这就需要采用方法的重写,方法重写又称方法覆盖

要求

子类重写父类方法,需要保证方法声明完全一致(方法名,参数,返回值类型需要保持一致)

举例

package com.itheima.mextends;

public class ExtendsDemo3 {
    /*
        方法重写的使用场景:
            子类继承了父类的方法, 但是子类不想原封不动的继承父类的方法逻辑
            想要修改或者是增强, 就可以重写父类方法.

        方法重载(Overload): 在同一个类中, 方法名相同, 参数不同, 与返回值无关
                                参数不同: 个数不同, 类型不同, 顺序不同.

        方法重写(Override): 在子父类中, 出现了方法声明完全一致的方法
                                (方法名, 参数, 返回值都需要和父类保持一致)

     */
    public static void main(String[] args) {
        Son s = new Son();
        s.love();
    }
}

class Father {
    void love() {
        System.out.println("送花");
        System.out.println("送自行车");
        System.out.println("送冰箱");
    }
}

class Son extends Father {
    @Override       // Override注解: 校验当前方法, 是否是重写的方法
    public void love() {
        super.love();
        System.out.println("送口红");
        System.out.println("送包");
    }
}

使用 @Override 注解可以校验当前方法, 是否是重写的方法

重写方法的使用可以使用 Ctrl + O 的快捷键,查看父类中有哪些方法可以重写

注意事项

  1. Java中继承的特点

Java只支持单继承,不支持多继承,但支持多层继承

  • 问题:为什么不支持多继承?

  • 回答:如果支持多继承,假设多个父类中有相同的方法结构,逻辑却不同,子类不知道该使用谁的逻辑

  • 问题:为啥多层继承可以?

  • 回答:多层继承的话,子类会知道使用重写后的方法逻辑

  1. 继承成员访问特点-构造方法

构造方法不能继承,子类需要手动编写自己的构造方法!

原因:构造方法要求方法名与类名相同,如果继承了父类的构造方法,就无法与类名相同了

问题:子类在初始化之前,是否需要先完成父类的初始化?

回答:需要!因为子类在初始化的时候,很有可能要用到父类中的数据,如果父类没有提前完成初始化,子类将无法使用父类数据

问题:子类是如何完成父类初始化的呢?

线索:什么方法是用于对象初始化的?

回答:构造方法,所以子类只要有办法调用到父类的构造方法,就能完成父类初始化

在所有的构造方法中,都默认隐藏了一句话 super();

通过这句代码,来访问父类的空参构造方法

super(); 可以调用父类的空参数构造方法

super(name, age); 我们可以传入参数,手动调用父类的带参数构造方法,从而完成数据的初始化

  1. 多态

同一个行为具有多个不同表现形式或形态的能力

这一概念有些晦涩,先不用纠结,理解:接口就是多态的一种

我们先掌握多态的使用前提,还有成员访问特点之后,再看这一概念

多态的前提条件

package com.itheima.polymorphism;

public class PolymorphismDemo1 {
    /*
        多态的前提:
            1. 继承 | 实现关系
            2. 方法重写
            3. 父类引用指向子类对象
     */
    public static void main(String[] args) {
        // 子类引用, 指向子类对象
        Zi z = new Zi();
        System.out.println(z.num);          // 20
        z.show();                           // Zi....show...

        // 父类引用, 指向子类对象 (以多态的形式创建对象)
        Fu f = new Zi();
        System.out.println(f.num);          // 10--成员变量看父类
        f.show();                           // Zi....show... --成员方法看子类
    }
}

class Fu {
    int num = 10;

    public void show() {
        System.out.println("Fu...show...");
    }
}

// 继承关系
class Zi extends Fu {
    int num = 20;

    // 方法重写
    @Override
    public void show() {
        System.out.println("Zi....show...");
    }
}

多态的成员访问特点

了解即可,面试不会问

  • 编译报错:Idea会提醒

  • 运行报错:大家后面都会做测试,能看出具体的问题

public class PolymorphismDemo1 {
    /*
        成员变量: 编译看左边(父类), 运行看左边(父类)
                因为是父类的引用, 所以访问存在局限性, 只能访问super空间中的数据.
    */
    public static void main(String[] args) {
        Fu f = new Zi();
        System.out.println(f.num);          // 10
    }
}


class Fu {
    int num = 10;
}

class Zi extends Fu {
    int num = 20;
}

public class PolymorphismDemo1 {
    /*
        成员方法: 编译看左边(父类), 运行看右边(子类)
                编译时检查方法在父类中是否存在
                    不存在: 编译出错
                    存在: 编译通过, 但运行的时候, 一定会执行子类的方法逻辑
    */
    public static void main(String[] args) {
        Fu f = new Zi();
        f.show();                                // Zi...show...
    }
}


class Fu {
    public void show() {
        System.out.println("Fu...show...");
    }
}

class Zi extends Fu {
    public void show() {
        System.out.println("Zi....show...");
    }
}

执行子类方法逻辑,这种设计可以保证方法的调用是有意义的,因为父类中的方法有可能是抽象方法

package com.itheima.polymorphism;

public class PolymorphismDemo2 {
    public static void main(String[] args) {
        // 接口类型变量, 指向了实现类对象 (多态的形式创建对象)
        Inter i = new InterImpl();
        i.show();
    }
}

interface Inter {
    void show();
}

class InterImpl implements Inter {

    @Override
    public void show() {
        System.out.println("实现类重写后的show方法...");
    }
}

多态创建对象,调用静态成员需要单独注意

package com.itheima.polymorphism;

public class PolymorphismDemo1 {
    /*
        静态成员: 编译看左边(父类), 运行看左边(父类)
                         static修饰的成员, 推荐使用类名调用
                         f.show();  ---> 字节码文件中 ---> Fu.show();
     */
    public static void main(String[] args) {
        Fu f = new Zi();
        f.show();                           // Fu....show...
    }
}

class Fu {
    public static void show() {
        System.out.println("Fu...show...");
    }
}

class Zi extends Fu {
    public static void show() {
        System.out.println("Zi....show...");
    }
}
  1. 多态的好处和弊端

好处

多态的好处:提高了程序的扩展性

abstract class Animal {
    public abstract void eat();
}

class Dog extends Animal {

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

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

class Cat extends Animal {

    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }

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

多态分类两种

  1. 对象多态

    将方法的形参定义为父类类型, 这个方法可以接收该父类的任意子类对象

    Animal a = new Dog();

    Animal a = new Cat();

  2. 行为多态

同一个行为, 具有多个不同表现形式或形态的能力

useAnimal方法中,调用 eat() 方法,因为接收到的对象不同

该方法的表现形式也不同

如果接收到 Cat 对象,输出的就是猫吃鱼

如果接收到 Dog 对象,输出的就是狗吃肉

弊端

多态的弊端 :不能直接使用子类的特有成员,想要使用的话,需要向下转型。

向上转型:从子到父(父类引用指向子类对象)

Fu f = new Zi();

向下转型:从父到子(将父类引用所指向的对象, 转交给子类类型)

Zi z = (Zi)f;
posted @ 2026-03-26 16:41  Kight-Tom  阅读(3)  评论(0)    收藏  举报