【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()构造器,会导致重复调用父类构造器
相关推荐
6***83051 小时前
微服务搭建----springboot接入Nacos2.x
java
郝学胜-神的一滴2 小时前
Linux命名管道:创建与原理详解
linux·运维·服务器·开发语言·c++·程序人生·个人开发
2501_941623322 小时前
C++高性能网络服务器与epoll实战分享:大规模并发连接处理与事件驱动优化经验
开发语言·php
晚风(●•σ )2 小时前
C++语言程序设计——11 C语言风格输入/输出函数
c语言·开发语言·c++
likuolei2 小时前
XML 元素 vs. 属性
xml·java·开发语言
X***48962 小时前
C源代码生成器
c语言·开发语言
自不量力的A同学2 小时前
Spring Boot 4.0.0 正式发布
java·spring boot·后端
梁正雄3 小时前
2、Python流程控制
开发语言·python
d***29243 小时前
【spring】Spring事件监听器ApplicationListener的使用与源码分析
java·后端·spring