- 第 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 对象时的执行顺序:
- 执行 super() 或 super(参数) 调用父类构造器
- 父类构造器初始化 name 和 age 属性
- 继续执行子类 Student 构造器中的代码
- 初始化 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关键字,指定访问父类的成员
javasuper.父类成员变量 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 基本介绍 | 代码示例
-
定义 : 方法重写是指
子类重新定义父类中已有的方法 -
目的 : 让
子类能够根据自身需求定制父类方法的具体实现 -
注意事项:
-
方法名、参数、返回类型必须与
父类中的方法完全一致 -
访问修饰符不能比
父类中的方法更严格:子类重写父类方法时,访问权限必须大于或等于父类该方法的权限
javapublic > 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()构造器,会导致重复调用父类构造器