当前位置:首页分享Java从入门到放弃(二十三):继承(上)

Java从入门到放弃(二十三):继承(上)

在面向对象编程(OOP)中,继承是一个核心概念,它允许我们创建一个新类(子类)来继承另一个类(父类)的属性和方法。这样做的好处是可以避免代码的重复,同时还可以扩展或修改继承来的行为和属性。

在前面的章节中,我们已经定义了Person类:

class Person {
    private String name;
    private int age;

    public String getName() {...}
    public void setName(String name) {...}
    public int getAge() {...}
    public void setAge(int age) {...}
}

现在,假设需要定义一个Student类。通过使用继承,我们可以在 Student 类中直接继承 Person 类的所有字段和方法,而无需再次定义 nameage 这两个字段及其方法。这样,Student 类的实例将自动拥有 Person 类的所有功能,并且还具备了 Student 类特有的 score 属性。

下面是使用继承后的 Student 类的代码示例:

class Person {
    private String name;
    private int age;

    // 构造器、getter和setter方法
    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;
    }
}

class Student extends Person {
    // 继承了Person类,不需要再次定义name和age字段及其方法
    private int score;

    // 构造器
    public Student(String name, int age, int score) {
        super(name, age); // 调用父类的构造器
        this.score = score;
    }

    // 新增score字段的getter和setter方法
    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }
}

 注意:子类自动获得了父类的所有字段,严禁定义与父类重名的字段!

在OOP的术语中,我们把Person称为超类(super class),父类(parent class),基类(base class),把Student称为子类(subclass),扩展类(extended class)。

继承树

在Java中,每个类都有一个继承体系,这使得对象可以共享和扩展公共的属性和方法。当我们在定义一个类时,如果没有明确指定继承自哪个类,Java编译器会自动将其继承自Object类。这是因为Object是Java中所有类的根类,它位于继承树的最顶端。因此,除了Object类本身,所有的类都会继承自某个父类。

当我们创建Student类时,我们使用extends关键字明确指出它继承自Person类。这样,Student类不仅继承了Person类的属性和方法,还可以添加或修改自己的特定行为。Student类的实例将拥有Person类的所有功能,并且增加了自己的score属性。

同样的,如果我们定义一个Teacher类,它也可以继承自Person类。这样,Teacher类将继承Person类的所有属性和方法,并可以添加自己的特定属性和方法。这种继承关系形成了一个层次结构,其中Object位于顶端,Person是中间的父类,而StudentTeacher是子类。

protected

在Java中,继承有一些访问控制的限制。特别是,子类不能直接访问父类的private成员。这是因为private访问修饰符限制了成员的访问范围仅在定义它们的类内部。这种设计是为了保护封装性,防止外部对类内部实现的直接访问和修改。

为了解决这个问题,我们可以将Person类的nameage字段的访问修饰符从private改为protected。这样做之后,这些字段就可以被子类Student访问了,因为protected访问级别允许子类及其子类访问这些成员。

class Person {
    protected String name; // 从private改为protected
    protected int age; // 从private改为protected
}

class Student extends Person {
    public String hello() {
        return "Hello, " + name; // 现在可以访问name字段
    }
}

super

在Java编程语言中,super是一个特殊的关键字,它被用来表示当前对象的直接父类。这个关键字在子类中特别有用,因为它允许子类访问父类的属性、方法和构造方法。当子类需要引用父类的成员时,可以使用super.fieldName的形式来明确指出要访问的是父类的字段,尽管在很多情况下,直接使用字段名也能达到同样的效果,因为编译器会自动解析到父类的字段。

例如,下面的Student类继承自Person类,并在hello方法中通过super.name访问父类的name字段:

class Student extends Person {
    public String hello() {
        return "Hello, " + super.name; // 访问父类的name字段
    }
}

然而,有时候必须显式地使用super关键字,尤其是在构造方法中。构造方法的第一个语句必须是对父类构造方法的调用,这是为了确保父类的成员变量能够被正确初始化。如果子类构造方法没有显式地调用父类构造方法,编译器会自动插入super();,这时候会默认调用父类的无参数构造方法。

来看这个例子:

public class Main {
    public static void main(String[] args) {
        Student s = new Student("Xiao Ming", 12, 89);
    }
}

class Person {
    protected String name;
    protected int age;

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

class Student extends Person {
    protected int score;

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

代码示例中,Student类的构造方法没有显式调用Person类的构造方法,这将导致编译错误,因为Person类没有无参数的构造方法。编译器试图插入super();,但是找不到匹配的Person构造方法,所以编译失败。

为了解决这个问题,Student类的构造方法必须显式地调用Person类中存在的一个构造方法,并提供相应的参数:

class Student extends Person {
    protected int score;

    public Student(String name, int age, int score) {
        super(name, age); // 正确调用Person类的构造方法
        this.score = score;
    }
}

super关键字在子类构造方法中的使用是必要的,尤其是当父类没有默认构造方法时。此外,需要注意的是,子类不会继承父类的构造方法,子类默认的构造方法是编译器自动生成的,而不是从父类继承的。因此,子类必须显式地调用父类的构造方法来确保正确的初始化。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧