什么是继承
java中提供extends关键字,使得两个类建立起继承和被继承的关系,其语法是:public class <子类> extends <父类>.
其中,继承的类称为子类(派生类)、被继承的类称为父类(基类、超类)
我们可以举例如下
有猫类和狗类,它们都继承自动物类。动物有自己的nickname,会睡觉、吃饭。而猫类和狗类都继承自动物类,且在动物类之上,猫类会舔毛,而狗类会摇尾巴。

使用继承的好处
- 可以把多个子类中重复的代码合并到父类,提高代码的复用性。
- 子类可以在父类的基础上按需添加更多的功能
什么时候适合用继承
- 当类与类之间存在相同(共性)的内容,并且满足子类是父类中的一种,就可以考虑使用继承来优化代码。
继承的特点
java中,只支持单继承、不支持多继承,但支持多层继承。
- 只支持单继承、不支持多继承:一个子类只能继承一个父类;(这点和CPP中完全不同)。
- 支持多层继承:A类可以继承父类B,而B类可以同时作为子类继承父类C。对于A来说,C是A的间接父类,B是A的直接父类。
- 每一个类都直接或间接继承自
Object类,Object类是JVM自行生成的。
子类可以继承父类什么内容
**非私有**(\public\、\protected\) |
**私有**(\private\) |
|
|---|---|---|
| 构造方法 | ❌子类不能继承 | ❌子类不能继承 |
| 成员变量 | ✅子类可以继承 | ✅子类可以继承(但不可直接访问) |
| 成员方法 | ✅子类可以继承 | ❌子类不能继承 |
说明:
- 成员变量在继承时,子类对象会包含完整的父类成员变量和子类自身定义的成员变量,每个对象实例在堆中拥有各自独立的内存空间。
- 如果子类没有重写继承的方法,子类和父类的方法在执行时指向方法区中的同一段方法代码;如果重写了,则各自在方法区中有自己的方法代码。
- 父类私有成员变量,子类可以继承但不可以直接访问:子类对象在内存中确实包含了父类的私有成员变量,但是在子类的代码中,不能直接用变量名来访问父类的私有成员。
- 只有父类中的虚方法(非
private修饰、非ststic修饰、非final修饰)、且方法非私有才可以被子类继承;准确来说,成员方法的继承,只有虚方法才可以被继承。
各种父类元素的继承情况
构造方法的继承情况
public class InheritanceDEMO {
public static void main(String[] args) {
Subclass s1 = new Subclass(); // ✅ 可以正常编译,因为 Subclass 存在对应的构造方法
// Subclass s2 = new Subclass("bingCat"); // ❌ 不可以正常编译,因为子类不会继承父类的构造方法
}
}
class Superclass {
String catName;
public Superclass() {}
public Superclass(String catName) {
this.catName = catName;
}
}
class Subclass extends Superclass {
public Subclass() {}
}
由于构造方法的方法名是和类名相同的,子、父类的类名不同,自然无法使用父类的构造方法,所以也不会继承其构造方法。
在如上的代码中,Subclass s1 = new Subclass();可以正常编译,因为 Subclass 存在对应的构造方法;Subclass s2 = new Subclass("bingCat");不可以正常编译,因为子类不会继承父类的构造方法.
如果强行编译如上代码中的Subclass s2 = new Subclass("bingCat");,则会报错如下:会认为没有对应的构造方法。

成员变量的继承情况
成员变量在继承的时候:非私有(public、protected)成员变量是可以继承且可以直接访问的;私有(private)成员变量是可以继承但是不能直接访问的。
public class InheritanceDEMO {
public static void main(String[] args) {
Subclass s = new Subclass();
}
}
class Superclass {
public String catName;
private int catAge;
}
class Subclass extends Superclass {
public String catFood;
public void showCat() {
System.out.println(catName); // ✅ 可以正常编译,因为 Subclass 会继承 Superclass 的成员变量,且非 private 修饰的成员变量可以直接访问
// System.out.println(catAge); // ❌ 不可以正常编译,因为被 private 关键字修饰的成员变量可以继承但是无法直接访问
}
}
如上的代码中,子类中的System.out.println(catName);语句可以正常编译,因为 Subclass 会继承 Superclass 的成员变量,且非 private 修饰的成员变量可以直接访问;System.out.println(catAge);语句不可以正常编译,因为被 private 关键字修饰的成员变量可以继承但是无法直接访问。
如果强行编译如上代码中的System.out.println(catAge);语句,则会报错如下:会清晰准确地告知具体的成员变量是private关键字修饰的。


如果父类中的成员变量是private修饰的,同样会被继承,但是无法直接访问。如果需要访问,可以在父类中定义对应的Getter或者Setter方法来使得子类可以访问、修改父类中private关键字修饰的成员变量。
成员方法的继承情况
虚方法:非private修饰、非ststic修饰、非final修饰。
父类会将自己的虚方法存储在虚方法表中,父类在被继承的时候,会将虚方法表被子类继承,继承时,子类会将自己本身的虚方法添加到继承的虚方法表中。这样有助于提升性能。
只有父类中的虚方法、且方法非私有才可以被子类继承。
public class InheritanceDEMO {
public static void main(String[] args) {
Subclass s = new Subclass();
s.superPublic(); // ✅ 正常编译,因为对应方法可以被调用,父类中的 superPublic 方法是 public 修饰的,可以被子类继承。
// s.superPrivate(); // ❌ 编译错误,因为对应方法是父类中的superPrivate,被private关键字修饰,子类无法继承
}
}
class Superclass {
public void superPublic() {}
private void superPrivate() {}
}
class Subclass extends Superclass {}
如上代码中,s.superPublic();语句可以正常编译,因为对应方法可以被调用,父类中的 superPublic 方法是 public 修饰的,可以被子类继承;s.superPrivate();语句无法被编译,因为对应方法是父类中的superPrivate,被private关键字修饰,子类无法继承。
如果强行编译s.superPrivate();语句,则报错如下:因为子类未继承superPrivate方法,自然找不到对应的方法。

在如上代码中,父类中的superPublic()可以被添加到虚方法表中。
各种继承元素的访问特点
成员变量的访问特点
遵循就近原则:先在局部位置中寻找,再在所属的类中寻找,再到父类中寻找。

public class InheritanceDEMO {
public static void main(String[] args) {
Subclass s = new Subclass();
System.out.println(s.cat);
}
}
class Superclass {
public String cat = "Sakura";
}
class Subclass extends Superclass {
public String cat = "leffay";
}

成员方法的访问特点
- 直接调用仍然满足就近原则;
- 也可以使用
super直接访问直接父类的成员方法。
如果子类重写父类的方法,也就是方法重写,但是在继承中子类和父类出现相同的方法名、相同的形式参数列表、返回类型相同子类小于等于父类、访问权限不能更加严格(可以相同的访问权限或者更加宽松),则子类的方法就是方法的重写。
需要说明的是,方法的重写需要满足如下的注意事项:
- 重写的方法名必须和被重写的方法名相同;
- 重写方法的形参列表必须和被重写方法的形参列表(包括顺序)相同;
- 重写方法的返回值类型,必须小于等于被重写方法的返回值类型;
- 重写方法的访问权限必须大于等于被重写方法的访问权限。
- 被重写方法被
private、ststic、final修饰时,不能重写。
方法是否能重写的本质,是被添加到虚方法表中的方法才能被重写。
方法的重写涉及到多态,会在后续学习多态的时候继续了解。
重写方法的时候使用**@Override**重写注解
@Override是放在重写的方法上,校验子类重写时语法是否正确。- 加上注解后如果有红色波浪线(idea中是在
@Override文字底下存在红色波浪线),则表示语法错误 - 重写方法时
@Override不是强制的,但是非常推荐都加上。
当子类重写父类方法时,子类的虚方法表中对应位置的方法引用会指向子类自己的实现,而不是父类的实现。未重写的方法则继续指向父类的方法实现。
构造方法的访问特点
父类中的构造方法不会被子类继承,不论是非私有还是私有构造方法。
子类中的所有构造方法,都会先默认指向父类中的无参构造方法,再执行自己的构造方法。
public class InheritanceDEMO {
public static void main(String[] args) {
Subclass s1 = new Subclass();
Subclass s2 = new Subclass(1);
}
}
class Superclass {
public Superclass() {
System.out.println("Superclass Default Constructor");
}
}
class Subclass extends Superclass {
public Subclass(int a) {
System.out.println("Subclass Parameterized Constructor");
}
public Subclass() {
System.out.println("Subclass Default Constructor");
}
}

总结如下
- 子类不能继承父类的构造方法,但是可以直接使用
super()调用对应的父类构造方法。 - 子类的构造方法中,默认会调用
super(),即父类的无参构造方法 - 如果需要使用父类的有参构造,需要自行手动编写代码。
this和super关键字
this指向当前对象,用于指向当前方法调用者的地址值。super指向父类对象,代表父类存储空间。
访问成员变量
public class InheritanceDEMO {
public static void main(String[] args) {
Subclass s = new Subclass();
s.showcat();
}
}
class Superclass {
public String cat = "bingCat";
}
class Subclass extends Superclass {
public String cat = "linCat";
public void showcat() {
String cat = "Sakuralaffey";
System.out.println(cat);
System.out.println(this.cat);
System.out.println(super.cat);
}
}

在子类中,只可以编写一个super,只能访问直接父类成员变量,不可以访问间接父类成员变量。
| 访问位置 | 语法 | 优先级 | 说明 |
|---|---|---|---|
| 局部变量 | <变量名> |
1 | 当前方法/代码块内 |
| 当前类成员 | this.<变量名> |
2 | 当前类的成员变量 |
| 直接父类成员 | super.<变量名> |
3 | 直接父类的可访问成员 |
| 间接父类成员 | 需转型或特定方法 | 4 | 需要通过其他方式访问 |
访问成员方法
不能通过对象引用使用this和super,this**和super只能在类的非静态方法内部使用**
public class InheritanceDEMO {
public static void main(String[] args) {
Subclass s = new Subclass();
// s.this.showMyCat();
// s.super.showMyCat();
}
}
class Superclass {
public void showMyCat() {
System.out.println("SakuraLaffey");
}
}
class Subclass extends Superclass {
@Override
public void showMyCat() {
System.out.println("bingCat");
}
}
如上代码中,s.this.showMyCat(); 语句和 s.super.showMyCat();语句是通过对象引用使用this和super,这样会导致编译出错,因为this和super只能在类的非静态方法内部使用。如果强制编译则会报错。

如果修改,则应该是如下代码
public class InheritanceDEMO {
public static void main(String[] args) {
Subclass s = new Subclass();
s.cats();
}
}
class Superclass {
public void showMyCat() {
System.out.println("SakuraLaffey");
}
}
class Subclass extends Superclass {
@Override
public void showMyCat() {
System.out.println("bingCat");
}
public void cats() {
this.showMyCat();
super.showMyCat();
}
}

访问构造方法

使用this()可以直接访问本类中其他构造方法。如果类存在继承关系,则如果使用this(),JVM就不会再默认去调用super()来构造对象。
且,无论使用this(),还是super(),都必须写在构造函数的第一行。
构造方法的调用顺序
public class OOPAbstract {
public static void main(String[] args) {
Cat cat = new Cat();
}
}
class Animal{
public Animal(){
System.out.println("Animal 类构造方法");
}
}
class Cat extends Animal{
public Cat(){
System.out.println("Cat 类构造方法");
}
}

如果两个类存在继承和被继承关系的时候,子类初始化的时候会默认调用父类的无参构造方法(编译器自动添加),如果父类没有,则子类的构造方法需要手动使用super(<基类构造方法的形参>)来初始化。如果子类实例化的时候没有调用父类的任意一个构造方法,则编译报错。
关忆点
- 调用顺序:父类构造 → 子类构造
- super() 的位置:必须是子类构造方法的第一条语句
- 隐式规则:
- 如果父类有无参构造,子类可不写super()(编译器自动添加)
- 如果父类只有有参构造,子类必须显式调用super(参数)
this和super的总结
| 关键字 | 访问成员变量 | 访问成员方法 | 访问构造方法 |
|---|---|---|---|
this |
使用this.<成员变量>,访问本类成员变量 |
使用this.<成员方法>(),访问本类成员方法 |
使用this(),访问本类构造方法 |
super |
使用super.<成员变量>,访问父类成员变量 |
使用super.<成员方法>(),访问父类成员方法 |
使用super(),访问父类构造方法 |