一、继承机制
继承机制是面向对象程序设计中实现代码复用的最重要手段,它允许程序员在保持原有类特性的基础上进行扩展,增加新功能,避免重复编写冗余代码。
举个生活中的例子:狗和猫都属于动物,它们都有姓名、年龄等属性,也都有睡觉、进食等行为。如果单独为狗和猫编写类,会出现大量重复代码,而通过继承就能高效解决这个问题。
注意:Java 不支持多继承,即一个子类不能同时继承两个及以上父类。
无继承的冗余代码
public class Cat {
public String name;
public int age;
public void sleep() {
System.out.println(this.name + "睡觉");
}
public void eat() {
System.out.println(this.name + "吃猫粮");
}
}
public class Dog{
public String name;
public int age;
public void sleep() {
System.out.println(this.name + "睡觉");
}
public void eat() {
System.out.println(this.name + "吃狗粮");
}
}
二、继承语法
Java 中实现继承的语法很简单:在子类类名后使用 extends 关键字,后跟父类类名。
语法格式:public class 子类名 extends 父类名 {}
有继承的优化代码示例
// 父类(基类):提取共性内容
public class Animal{
public String name;
public int age;
public void sleep() {
System.out.println(this.name + "睡觉");
}
public void eat() {
System.out.println(this.name + "吃饭");
}
}
// 子类(派生类):继承父类共性,扩展自身特有功能
public class Dog extends Animal{
// 子类特有方法:狗叫
public void bark() {
System.out.println(this.name + "汪汪汪");
}
}
继承的核心说明
- 子类会自动继承父类的所有非私有成员变量和成员方法(私有成员无法直接访问)。
- 子类应当添加自身独有的成员(变量/方法),否则就失去了继承的意义(直接使用父类即可)。
三、子类访问父类的成员
子类访问成员(变量/方法)时,会遵循「就近原则」,同时可以通过 super 关键字强制访问父类成员。
1. 子类访问父类的成员变量
public class Base {
public int a;
public int b;
}
public class Derived extends Base {
public int a; // 与父类成员变量重名
public int c;
}
访问规则:
- 当访问的成员变量子类存在时,优先访问子类的成员变量。
- 当访问的成员变量子类不存在时,会向上查找并访问父类的成员变量。
- 当访问的成员变量子类和父类都不存在时,编译器直接报错。
2. 子类访问父类的成员方法
public class Base {
public void Method1() {
System.out.println("Method1方法被调用");
}
}
public class Derived extends Base {
public void Method2() {
System.out.println("Method2方法被调用");
}
public void Test() {
// 父类的方法被调用(子类无此方法,向上查找)
Method1();
// 子类的方法被调用(子类有此方法,优先访问)
Method2();
// Method3(); // 报错:子类和父类均无此方法
}
}
访问规则(与成员变量一致):
- 当访问的成员方法子类存在时,优先访问子类的成员方法。
- 当访问的成员方法子类不存在时,会向上查找并访问父类的成员方法。
- 当访问的成员方法子类和父类都不存在时,编译器直接报错。
3. super 关键字:强制访问父类成员
当子类成员与父类成员重名(尤其是方法重写)时,若想强制访问父类的成员,就需要使用 super 关键字,它的作用是引用当前子类对象中所包含的父类对象。
示例1:访问父类的成员变量和成员方法
public class Base {
public int a = 10;
public void Method1() {
System.out.println("父类的 Method1 方法被调用");
}
}
public class Derived extends Base {
public int a = 20; // 与父类重名
// 方法重写:子类方法与父类方法签名(方法名+参数列表)一致
public void Method1() {
System.out.println("子类的 Method1 方法被调用");
}
public void Test() {
// 访问子类的a:20
System.out.println(this.a);
// 访问父类的a:10(强制通过super访问)
System.out.println(super.a);
// 调用子类的Method1
this.Method1();
// 调用父类的Method1(强制通过super访问)
super.Method1();
}
}
示例2:调用父类的构造方法
super(参数) 还可以在子类构造方法中调用父类的指定构造方法,解决父类无默认构造方法的初始化问题。
public class Base {
int a = 10;
// 父类自定义有参构造方法(此时编译器不会生成默认无参构造方法)
public Base(int a) {
this.a = a;
}
}
public class Derived extends Base {
public Derived(int a) {
// 必须写在子类构造方法的第一行,调用父类的有参构造方法
super(a);
}
}
super 关键字的使用注意事项
super(参数)用于调用父类的指定构造方法,super()用于调用父类的无参构造方法。super()和this()(调用子类自身其他构造方法)都必须写在构造方法的第一行,两者不能同时存在。super只能在类的非静态方法中使用,用于访问父类的非静态成员变量和方法(静态成员属于类,不依赖对象)。
四、继承关系中的代码块执行顺序
当创建子类对象时,会触发父类和子类的多个代码块(静态代码块、实例代码块、构造方法)执行,执行顺序有严格规定。
执行顺序(优先级从高到低)
- 执行父类的静态代码块(仅第一次创建子类对象时执行,后续重复创建不再执行)。
- 执行子类的静态代码块(仅第一次创建子类对象时执行,后续重复创建不再执行)。
- 执行父类的实例代码块(每次创建对象都执行,在构造方法前执行)。
- 执行父类的构造方法(每次创建对象都执行,完成父类对象初始化)。
- 执行子类的实例代码块(每次创建对象都执行,在子类构造方法前执行)。
- 执行子类的构造方法(每次创建对象都执行,完成子类对象初始化)。
代码示例与执行结果
// 父类
public class Base {
public static int a;
public int b;
// 父类静态代码块
static {
System.out.println("父类静态代码块被执行---1");
}
// 父类实例代码块
{
System.out.println("父类实例代码块被执行---2");
}
// 父类构造方法
public Base(int b) {
this.b = b;
System.out.println("父类构造方法代码块被执行---3");
}
}
// 子类
public class Derived extends Base {
// 子类静态代码块
static {
System.out.println("子类静态代码块被执行---4");
}
// 子类实例代码块
{
System.out.println("子类实例代码块被执行---5");
}
// 子类构造方法
public Derived(int b) {
super(b);
System.out.println("子类构造方法代码块被执行---6");
}
}
运行结果(创建子类对象 new Derived(10))
父类静态代码块被执行---1
子类静态代码块被执行---4
父类实例代码块被执行---2
父类构造方法代码块被执行---3
子类实例代码块被执行---5
子类构造方法代码块被执行---6
五、final 关键字
final 关键字表示「最终的、不可修改的」,在继承场景中常用,主要有以下核心用途:
final修饰变量:使其变成常量(值不可修改),建议常量名全部大写,多个单词用下划线分隔。public final int MAX_AGE = 150;final修饰方法:使其不能被子类重写,保证方法的逻辑不可被修改。public final void sleep() { System.out.println("不可被重写的睡觉方法"); }final修饰类:使其不能被继承(该类无子类),例如 Java 中的String类就是final类。public final class FinalClass { // 此类无法被其他类继承 }