Java 继承 (Inheritance)
继承是面向对象三大特性(封装、继承、多态)之一。它是一种代码复用 和类关系建模的机制。
一、继承的基本概念
-
定义 :Java使用
extends关键字建立类与类之间的继承关系。 -
语法:
java
public class 子类 extends 父类 { // 子类特有的属性和方法 } -
术语:
-
子类 (Subclass):也叫派生类。继承的类。
-
父类 (Superclass):也叫超类、基类。被继承的类。
-
-
目的:
-
代码复用:将多个子类共有的属性和行为抽取到父类中,避免代码重复。
-
功能扩展:子类在继承父类功能的基础上,可以添加自己特有的功能。
-
建立"is-a"关系 :继承代表一种"子类是父类的一种"的逻辑关系(例如:
Studentis aPerson)。
-
二、何时使用继承?
必须同时满足以下两个条件:
-
存在共性内容:多个类之间存在大量相同的属性或方法。
-
逻辑关系成立 :在逻辑上满足 "子类是父类的一种" 的关系。不能仅仅为了复用代码而滥用继承。
三、继承的特点
-
单继承 :Java只支持单继承,一个类只能有一个直接父类。
-
不支持多继承 :一个类不能同时
extends多个父类。(但可以通过接口实现类似功能)。 -
支持多层继承:继承可以传递(A继承B,B继承C)。
java
class A extends B {} // A的直接父类是B class B extends C {} // B的直接父类是C // 最终,A间接继承了C。 -
所有类的根 :在Java中,每个类都直接或间接继承自
java.lang.Object类(Object类是所有类的超类)。
四、继承中成员的访问特点
1. 成员变量访问
遵循 "就近原则"。
-
查找顺序 :局部变量 → 子类成员变量 (this) → 父类成员变量 (super)。
-
关键字:
-
this.变量名:明确指定访问本类的成员变量。 -
super.变量名:明确指定访问父类的成员变量。
-
示例:
java
class Person {
String name = "PersonName";
}
class Student extends Person {
String name = "StudentName";
public void show(String name) {
System.out.println(name); // 局部变量,就近原则
System.out.println(this.name); // 子类的成员变量 "StudentName"
System.out.println(super.name); // 父类的成员变量 "PersonName"
}
}
2. 成员方法访问
访问规则与成员变量相同(就近原则)。关键在于方法的重写 (Override)。
五、方法的重写 (Override)
1. 定义与时机
-
定义 :在继承体系中,子类 出现了一个与父类方法声明(方法名、参数列表)完全相同的方法。
-
时机 :当从父类继承来的方法不能满足子类特定的需求时,就需要重写。
2. 格式与注解
-
格式:子类方法的声明(方法名、参数列表)必须与父类方法一致。
-
@Override注解:-
这是一个安全校验注解,加在子类重写的方法上。
-
作用:由编译器检查该方法是否真的成功重写了父类方法。如果由于拼写错误等原因导致没有正确重写,编译器会报错。
-
强烈建议重写时都加上此注解,保证代码安全、意图清晰。
-
3. 重写的核心规则 (重要!)
-
方法名与形参列表 :必须与父类被重写的方法完全相同。
-
访问权限 :子类方法的访问权限必须 >= 父类方法的访问权限。
-
public>protected> 默认(package-private) >private -
例如:父类是
protected,子类可以是protected或public,但不能是默认或private。
-
-
返回值类型:
-
基本数据类型/
void:必须完全相同。 -
引用数据类型:子类方法的返回值类型可以是父类方法返回值类型的子类(即"小于等于")。
-
-
重写范围 :只有被声明为 非
private、非final、非static的实例方法(通常指进入了"虚方法表"的方法)才能被重写。
示例:
java
class Animal {
public void eat() {
System.out.println("动物吃东西");
}
protected Animal getInstance() {
return new Animal();
}
}
class Cat extends Animal {
@Override // 正确:重写了eat方法
public void eat() {
System.out.println("猫吃鱼");
}
@Override // 正确:返回值类型是Animal的子类
public Cat getInstance() {
return new Cat();
}
}
六、继承中构造方法的访问特点
-
构造方法不能被继承:子类不能继承父类的构造方法。
-
隐式调用 :子类任何构造方法的第一行,都默认隐含了
super(),即先调用父类的无参构造。java
public Student() { super(); // 默认存在,且必须在第一行 // ... 子类构造后续代码 } -
显式调用 :如果父类没有无参构造,或者你想调用父类的有参构造,必须在子类构造方法的第一行使用
super(参数)明确指定。 -
this(...)与super(...)在构造方法中不能共存,因为它们都要求位于第一行。
七、this 与 super 关键字总结
| 关键字 | 含义 | 调用位置 |
|---|---|---|
this |
代表当前对象的引用(在内存中的地址)。 | 1. 访问本类成员变量:this.变量名 2. 访问本类成员方法:this.方法名() 3. 访问本类其他构造器:this(...) (必须第一行) |
super |
代表父类存储空间的标识(可以理解为父类对象的引用,但不等同于父类对象)。 | 1. 访问父类成员变量:super.变量名 2. 访问父类成员方法:super.方法名() 3. 访问父类构造器:super(...) (必须第一行) |
核心区别 :this 指向当前实例本身,super 是子类中访问父类成员的桥梁,它不是一个独立的对象。
java
package extensdemotext;
public class Employee {
private String id;
private String name;
private String position;
public Employee() {
}
public Employee(String id, String name,String position) {
this.id = id;
this.name = name;
this.position = position;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPosition(){
return position;
}
public void setPosition(String position){
this.position = position;
}
public void work(){
System.out.println("员工在工作");
}
}
package extensdemotext;
public class Teacher extends Employee{
public Teacher() {
}
public Teacher(String id, String name,String position) {
super(id, name, position);
}
@Override
public void work(){
System.out.println("教研部员工正在工作");
}
}
package extensdemotext;
public class AdminStaff extends Employee {
public AdminStaff() {
}
public AdminStaff(String id, String name, String position) {
super(id, name, position);
}
@Override
public void work(){
System.out.println("行政部员工在工作");
}
}
package extensdemotext;
public class Lecturer extends Teacher{
public Lecturer() {
}
public Lecturer(String id, String name,String position) {
super(id, name, position);
}
@Override
public void work(){
System.out.println(getName()+"在上课");
}
}
package extensdemotext;
public class Tutor extends Teacher{
public Tutor() {
}
public Tutor(String id, String name,String position) {
super(id, name,position);
}
@Override
public void work(){
System.out.println(getName()+"在批改作业");
}
}
package extensdemotext;
public class Maintainer extends AdminStaff{
public Maintainer() {
}
public Maintainer(String id, String name,String position) {
super(id, name,position);
}
@Override
public void work(){
System.out.println(getName()+"在维护设施");
}
}
package extensdemotext;
public class Buyer extends AdminStaff{
public Buyer() {
}
public Buyer(String id, String name,String position) {
super(id, name,position);
}
@Override
public void work(){
System.out.println(getName()+"在采购");
}
}
package extensdemotext;
public class Test {
public static void main(String[] args) {
Lecturer l = new Lecturer("001","zhangsan","讲师");
//System.out.println(l.getId()+", "+l.getName()+", "+l.getPosition());
Tutor t = new Tutor("002","lisi","助教");
Maintainer m = new Maintainer("003","wangwu","维护专员");
Buyer b = new Buyer("004","zhaoniu","采购专员");
l.work();
t.work();
m.work();
b.work();
}
}