【Java 基础】面向对象 - 继承

- 第 156 篇 -
Date: 2025 - 11- 24
Author: 郑龙浩(仟墨)

【Java 基础】面向对象 - 继承

1 基本介绍

提高代码复用性,建立类之间的层次关系

1)继承的建立

  • 使用 extends关键字建立父子关系
  • 格式:public class 子类 extends 父类

2)继承内容

  • 能继承 :父类的非私有成员(成员变量、成员方法)

    私有成员不能继承,但是,并不意味着不存在,私有成员在子类中存在,只是无法在子类中访问,但是可以在父类中访问

    • 也就是public修饰的成员不可以被子类访问
    • 但是protected修饰的成员可以被子类访问 ,也可以被子类的子类(孙)访问
  • 不能继承:父类的私有成员(但可通过公有方法间接访问)

3) 对象创建特点

  • 子类对象由子类 + 父类共同完成(说白了就是:子类有父类中的成员和方法 + 本身就有的成员和方法)
  • 体现了面向对象的代码复用特性

4)单继承

  • Java是单继承:一个子类只能有一个直接父类(不可以继承多个)
  • 但是可以多层继承
  • 就近原则:优先访问自己的类,如果自己类中没有才会访问父类,若还没有,直接报错

2 super() 调用父类构造器

2.1 super() 是什么?

  • super() 是用来调用父类(People)构造器的关键字
  • 它确保从父类继承的属性(如 name 和 age)能够正确初始化
  • 必须作为构造器中的第一条语句

2.2 执行过程

创建 Student 对象时的执行顺序:

  1. 执行 super() 或 super(参数) 调用父类构造器
  2. 父类构造器初始化 name 和 age 属性
  3. 继续执行子类 Student 构造器中的代码
  4. 初始化 id 属性

2.3 调用类型

  • 无参调用: super() 调用 People()
  • 带参调用: super(name, age) 调用 People(String name, int a

2.4 为什么要调用父类构造器?

  • 确保继承的属性得到正确初始化
  • 保持对象的完整性
  • 避免使用未初始化的父类属性

2.5 示例:

People.java - 父类

java 复制代码
package zhenglonghao.extends1;
// 父类
public class People {

    // 1. 成员(全部私有)
    private static int cnt = 0; // 人数,静态成员变量(类变量)
    private String sex; // 性别,实例成员变量
    private String name; // 姓名,实例成员变量
    private int age; // 年龄,实例成员变量

    // 2. 构造方法
    public People() {}
    public People(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 3. getter 和 setter
    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;
    }
}

Student.java - 子类

java 复制代码
package zhenglonghao.extends1;

public class Student extends People {
    // People中的其他方法会继承过来
    // People中的私有成员不会继承过来,但是存在,只是无法在当前类中访问(因为用的private修饰)
    // People中的公有成员会继承过来,在当前类中可以访问

    // 1. 成员(本类)
    private String id; // 学号(属于本类)

    // 2. 构造器

    public Student() {
        super(); // 调用父类的构造器,意思就是:创建一个Student对象时,会自动调用父类的无参构造器
    }

    // 调用下面的构造器创建对象时,执行过程如下:
    //    创建 Student 对象时:
    //    先执行 super(name, age) 调用父类构造器
    //    父类构造器初始化 name 和 age 属性
    //    再执行子类 Student 构造器中的代码
    //    初始化 id 属性
    public Student(String name, int age, String id) {
        super(name, age); // 调用父类的构造器:创建一个Student对象时,会自动调用父类的有参构造器
        this.id = id; // 本类的id = 本方法中的id
    }

    // 3. getter 和 setter 方法
    public String getId () {
        return this.id; // this可忽略
    }
    public void setId(String id) {
        this.id = id; // 本类的id = 本方法中的id
    }
}

3 权限修饰符

一般情况下,只使用private 与 public,另外两个需谨慎使用,不要乱用

  • private:只能在本类中访问
  • 缺省(默认):可在本类和同一个包中的类中访问
  • protected:可在本类、同一包中的类以及子类中访问
  • public:可在任意位置访问

4 super关键字

如果子父类中,出现了重名的成员/方法,会优先使用子类的成员/方法

如果此时一定要在子类中使用父类的成员/方法,怎么办????

  • 使用super关键字,指定访问父类的成员

    java 复制代码
    super.父类成员变量
    super.父类成员方法
java 复制代码
package zhenglonghao.extends2;
public class Test {
    public static void main(String[] args) {

    }
}

// 父类
class Father {
    String name = "Father的name";

    public void show() {
        System.out.println("父类的show方法");
    }
}

// 子类
class Son extends Father {
    String name = "Son的name";

    public void show() {
        String name = "show的name";
        System.out.println(name); // show的name
        System.out.println(this.name); // son的name
        System.out.println(super.name); // father的name
        super.show(); // 调用父类的show方法
    }
}

5 方法重写

5.1 基本介绍 | 代码示例

  • 定义 : 方法重写是指 子类 重新定义 父类 中已有的 方法

  • 目的 : 让 子类 能够根据自身需求定制 父类 方法的具体实现

  • 注意事项:

    • 方法名、参数、返回类型必须与 父类 中的方法完全一致

    • 访问修饰符不能比 父类 中的方法更严格:

      子类重写父类方法时,访问权限必须大于或等于父类该方法的权限

      java 复制代码
      public > protected > 缺省(什么也不写)
    • 使用 @Override 注解来标识重写的方法

      可以校验重写的方法是否正确(名字、参数、返回类型是否正确)

      如果错了,就会报错

    • 私有方法、静态方法不能被重写,否则报错

  • 示例:

Test文件

java 复制代码
package zhenglonghao.opp.extends1;

public class Test {
    public static void main(String[] args) {
        // 使用父类方法
        Parent parent = new Parent();
        parent.display(); // 打印: Parent display
        // 使用子类方法
        Child child = new Child();
        child.display(); // 打印: Child display
    }
}

// 父类
class Parent {
    public void display() {
        System.out.println("Parent display\n");
    }
}
// 子类
class Child extends Parent {
    @Override // 覆盖父类的方法
    // @Override 的作用:告诉编译器,这个方法是子类的方法,而不是父类的方法
    // 这样约束了这个方法的名字和参数必须与父类一致,如果不一致,就会报错
    // 但是不写@Override的话,就不会当作「方法重写」,也就不会报错
    public void display() {
        System.out.println("Child display\n");
    }
    // 如果方法是下面这样,就会报错 --> 因为方法名字不同
    // public void show() {
    //    System.out.println("Child show");
    // }
}

5.2 应用场景是什么?

子类重写Objict类的toString()方法,以便于返回对象的内容

如下:System.out.println(s);,就等于s.toString,打印的是「类的全限定名 + @ + 对象的地址」,但是如果想直接打印出所有的成员,就需要重写方法了

java 复制代码
package zhenglonghao.opp.extends1;

public class Test2 {
    public static void main(String[] args) {
        Student s = new Student("张三", 18); // zhenglonghao.opp.extends1.Student@23fc625e
        System.out.println(s); // 直接打印对象的话,默认会调用Object的toString方法,所以会打印 类的全限定名 + @ + 对象的地址
        // 因此需要重写toString方法,让其打印对象中的所有成员
    }
}

class Student {
    // 成员
    String name;
    int age;

    // 构造器
    ......

    // getter setter
    ......

}

重写方法后

直接打印

java 复制代码
Student{name=张三, age=18}
java 复制代码
package zhenglonghao.opp.extends1;

public class Test3 {
    public static void main(String[] args) {
        Student s = new Student("张三", 18); // zhenglonghao.opp.extends1.Student@23fc625e
        System.out.println(s); // 因为重写了toString方法,所以会打印对象中的所有成员Student{name=张三, age=18}
    }
}

class Student2 {
    // 成员
    String name;
    int age;

    // 构造器
    ...

    // 重写方法toString --> 目的:打印所有成员
    @Override
    public String toString() {
        return "Student{name=" + name + ", age=" + age + "}";
    }

    // getter setter
    ...

}

6 子类构造器 | super()的应用

6.1 子类构造器的注意事项:

  • 子类的全部构造器,必须先调用父类的构造器,再执行自己的构造器(默认也是这样)
  • 也可以使用super(),它会调用父类的构造器(且一定要写在该子类的构造器的前面)
  • 如果不写,子类全部构造器的第一行代码一定默认是:super()
  • 如果父类没有无参构造器,则必须在子类构造器的第一行手写一个super(),指定去调用父类的有参构造器 | 否则一定报错(不写第一行,也会报错)
  • 如果继承,在子类构造器中,有参构造器,则必须写上super(父类成员1, 父类成员2),否则父类成员的内容是空(NULL)

6.2 例子

People.java 父类

java 复制代码
package zhenglonghao.opp.extends2;

// 父类
public class People {
    // 1 成员
    private String name;
    private int age;

    // 2 构造器
    public People() {}
    public People(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 3 方法
    public String getName() {
        return name;
    }

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

Student.java 子类

下面的代码中,都用了super()

  • 对于无参构造器来说无所谓,写于不写都可以,因为即使不写也会默认
  • 但是对于有参构造器来说,必须要写super(),否则父类中的成员不会被赋值,也就是里面为空,NULL
java 复制代码
package zhenglonghao.opp.extends2;

// 子类
public class Student extends People {
    // 1 子类成员
    int score;
    // 2 构造器
    public Student() {
        super(); // 调用父类的无参构造器(对于无参构造器来说,写与不写都是一样的,因为会默认调用)
    }
    public Student(String name, int age, int score) {
        super(name, age); // 调用父类的有参构造器,对于有参构造器来说,就必须要写super了,否则name与age是不会初始化的,也就是为NULL,为空
        this.score = score;
    }
    // 3 Getter Setter
    public int getScore() {
        return score;
    }

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

7 兄弟构造器 | this()

7.1 作用

  • this() 用于调用当前类的其他构造器
  • 实现构造器之间的代码复用,避免重复代码

7.2 使用方法

  • 只能在构造器中使用
  • 必须放在构造器的第一行
  • 通过传递不同参数来调用相应的重载构造器

示例

java 复制代码
public class Student {
    private String name;
    private int age;
    private int score;
    
    // 无参构造器
    public Student() {
        this("Unknown", 0, 0); // 调用三参构造器
    }
    
    // 三参构造器
    public Student(String name, int age, int score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }
}

7.3 一般应用场景

Student.java文件 - 子类

下面第2个构造器,可以默认id为0000001

java 复制代码
package zhenglonghao.opp.extends2;

/***
 * 练习兄弟构造器
 */
// 子类
public class Student extends People {
    // 1 子类成员
    int score;
    String id;
    // 2 构造器
    public Student() {
        super(); // 调用父类的无参构造器(对于无参构造器来说,写与不写都是一样的,因为会默认调用)
    }
    // 如果调用了当前构造器,虽然没有存储id,但是id会默认为 0000001
    public Student(String name, int age, int score) {
        // 如果写了 this(name, age, score, "0000001") 就不要再写super(),否则会报错,因为super()必须在this()之前调用
        // 下面这一行的意思是: 调用下面的有参构造器,并且默认id为0001
        this(name, age, score, "0000001");
    }
    public Student(String name, int age, int score, String id) {
        super(name, age); // 调用父类的有参构造器,对于有参构造器来说,就必须要写super了,否则name与age是不会初始化的,也就是为NULL,为空
        this.score = score;
        this.id = id;
    }
    // 3 Getter Setter
    public int getScore() {
        return score;
    }

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

8 super()、this() 注意事项

  • 这俩必须写在构造器中,且也必须写在构造器的第一行
  • 且两者不能同时出现在同一个构造器中
  • 有this()的构造器必须在有super()的构造器前面,否则会调用两边super()构造器,会导致重复调用父类构造器
相关推荐
来杯@Java14 分钟前
图书管理系统(基于springboot+vue前后端分离的项目)计算机毕业设计java
java·spring boot·spring·vue·毕业设计·mybatis·课程设计
卷毛的技术笔记1 小时前
告别硬编码!Spring AI Alibaba 实现 AI Agent 智能工具调用(Tool Calling)
java·人工智能·后端·python·spring·ai编程
编程大师哥1 小时前
匿名函数 lambda + 高阶函数
java·python·算法
isyangli_blog1 小时前
OpenDayLight (Carbon 版本) 启动与组件安装
开发语言·php
vb2008111 小时前
FastAPI APIRouter
开发语言·python
Benszen1 小时前
KVM虚拟化解决方案
开发语言·perl
会编程的土豆1 小时前
Go 语言反射(Reflection)详解
开发语言·后端·golang
東雪木1 小时前
多线程与并发编程 专属复习笔记
java·开发语言·笔记·java面试
adrninistrat0r1 小时前
Java调用链MCP分析工具
java·python·ai编程
杨充2 小时前
1.3 浮点型数据设计灵魂
开发语言·python·算法