面向对象:继承

面向对象:继承

一、继承机制

继承机制是面向对象程序设计中实现代码复用的最重要手段,它允许程序员在保持原有类特性的基础上进行扩展,增加新功能,避免重复编写冗余代码。

举个生活中的例子:狗和猫都属于动物,它们都有姓名、年龄等属性,也都有睡觉、进食等行为。如果单独为狗和猫编写类,会出现大量重复代码,而通过继承就能高效解决这个问题。

注意: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 + "汪汪汪");
    }
    
}

继承的核心说明

  1. 子类会自动继承父类的所有非私有成员变量和成员方法(私有成员无法直接访问)。
  2. 子类应当添加自身独有的成员(变量/方法),否则就失去了继承的意义(直接使用父类即可)。

三、子类访问父类的成员

子类访问成员(变量/方法)时,会遵循「就近原则」,同时可以通过 super 关键字强制访问父类成员。

1. 子类访问父类的成员变量

public class Base {
    public int a;
    public int b;
}

public class Derived extends Base {
    public int a; // 与父类成员变量重名
    public int c;
}

访问规则:

  1. 当访问的成员变量子类存在时,优先访问子类的成员变量。
  2. 当访问的成员变量子类不存在时,会向上查找并访问父类的成员变量。
  3. 当访问的成员变量子类和父类都不存在时,编译器直接报错。

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(); // 报错:子类和父类均无此方法
    }

}

访问规则(与成员变量一致):

  1. 当访问的成员方法子类存在时,优先访问子类的成员方法。
  2. 当访问的成员方法子类不存在时,会向上查找并访问父类的成员方法。
  3. 当访问的成员方法子类和父类都不存在时,编译器直接报错。

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 关键字的使用注意事项

  1. super(参数) 用于调用父类的指定构造方法,super() 用于调用父类的无参构造方法。
  2. super()this()(调用子类自身其他构造方法)都必须写在构造方法的第一行,两者不能同时存在。
  3. super 只能在类的非静态方法中使用,用于访问父类的非静态成员变量和方法(静态成员属于类,不依赖对象)。

四、继承关系中的代码块执行顺序

当创建子类对象时,会触发父类和子类的多个代码块(静态代码块、实例代码块、构造方法)执行,执行顺序有严格规定。

执行顺序(优先级从高到低)

  1. 执行父类的静态代码块(仅第一次创建子类对象时执行,后续重复创建不再执行)。
  2. 执行子类的静态代码块(仅第一次创建子类对象时执行,后续重复创建不再执行)。
  3. 执行父类的实例代码块(每次创建对象都执行,在构造方法前执行)。
  4. 执行父类的构造方法(每次创建对象都执行,完成父类对象初始化)。
  5. 执行子类的实例代码块(每次创建对象都执行,在子类构造方法前执行)。
  6. 执行子类的构造方法(每次创建对象都执行,完成子类对象初始化)。

代码示例与执行结果

// 父类
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 关键字表示「最终的、不可修改的」,在继承场景中常用,主要有以下核心用途:

  1. final 修饰变量:使其变成常量(值不可修改),建议常量名全部大写,多个单词用下划线分隔。
    public final int MAX_AGE = 150;
    
  2. final 修饰方法:使其不能被子类重写,保证方法的逻辑不可被修改。
    public final void sleep() {
        System.out.println("不可被重写的睡觉方法");
    }
    
  3. final 修饰:使其不能被继承(该类无子类),例如 Java 中的 String 类就是 final 类。
    public final class FinalClass {
        // 此类无法被其他类继承
    }
    
面向对象:封装 2025-07-04
面向对象:多态 2025-07-06

评论区