【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()构造器,会导致重复调用父类构造器
相关推荐
Scott9999HH14 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔15 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社16 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海16 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖16 小时前
JDK 26 新特性详解
java·开发语言
马优晨17 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区19 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大19 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai20 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说20 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json