子类继承父类各自静态区和非静态区和构造方法的执行顺序

本文通过一个简单的Java示例程序演示了在子类继承父类的过程中,父类与子类中静态区、非静态区及构造方法的执行顺序。首先加载并执行父类与子类的静态区代码,随后实例化过程中按父类非静态区、父类构造方法、子类非静态区、子类构造方法的顺序执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先引入一个问题:
子类在继承父类的时候,父类的静态区,非静态区,构造方法,子类的静态区,非静态区,构造方法的执行顺序是怎么样的。
写一个简单的demo来验证一下:
父类:

public class Father {
    static{
        System.out.println("这是父类静态区");
    }
    {
        System.out.println("这是父类非静态区");
    }
    public Father(){
        System.out.println("这是父类的构造方法");
    }
}

子类:

public class Son extends Father{
    static{
        System.out.println("这是子类静态区");
    }
    {
        System.out.println("这是子类非静态区");
    }
    public Son(){
        System.out.println("这是子类的构造方法");
    }
}

主类:

public class Main {

    public static void main(String[] args) {
        Son son = new Son();
    }
}

运行结果是:

这是父类静态区
这是子类静态区
这是父类非静态区
这是父类的构造方法
这是子类非静态区
这是子类的构造方法

可以看到执行顺序:
父类静态区 -> 子类静态区 -> 父类非静态区 -> 父类构造方法 -> 子类非静态区 -> 子类构造方法

那么原理是什么呢?

1.在new Son的对象之前要装载Son这个对象,根据类的加载模式,首先要装载Son的父类Father,所以先完成父类和子类的静态动作。
2.装载完类的后就需要实例化Son这个对象,在实例化Son的对象时也要先实例化父类Father,所以先执行父类的非静态区和构造方法(成员实例化),再去执行子类的非静态区和构造方法。

<think>我们正在讨论Java中通过继承父类并重写方法的方式创建子类时,如何明确体现对父类继承。重点在于理解继承机制,特别是方法重写(覆盖)与重载的区别,以及多态性的体现。 根据引用[1]引用[2]的内容,我们可以总结如下: - 方法重写(覆盖)是子类父类允许访问的方法的实现过程进行重新编写,返回值形参都不能改变。当子类对象调用重写的方法时,调用的是子类的方法,而不是父类中被重写的方法。这体现了多态性。 - 方法重载是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。重载不会影响父类方法的调用,因为重载的方法被视为新的方法(与父类方法签名不同)。 现在,我们结合用户的问题:“在通过继承父类并重写方法的方式创建子类时,如何明确体现对父类继承”。 ### 体现继承的关键点 1. **子类拥有父类的属性方法**:子类在创建时,会隐式或显式地调用父类的构造器,从而初始化父类定义的属性。同时,子类继承父类所有可继承的方法(private且final)。 2. **方法重写(覆盖)**:这是体现继承的重要方式。子类可以重写父类的方法,从而改变该方法的行为。但是,重写的方法必须遵循以下规则: - 方法名、参数列表必须与父类方法完全相同。 - 返回类型可以是父类方法返回类型的子类(协变返回类型)。 - 访问权限不能比父类方法更严格(例如,父类方法是public,子类重写时不能是protected或private)。 - 不能抛出比父类方法更多的异常(即子类方法抛出的异常必须是父类方法抛出的异常的子类或不抛出异常)。 3. **使用`super`关键字**:在子类中,可以通过`super`关键字显式调用父类的方法或构造器,这直接体现了继承关系。例如: - 在子类构造器中,使用`super(...)`调用父类构造器(必须放在子类构造器的第一行)。 - 在子类重写的方法中,可以使用`super.方法名(...)`调用父类被重写的方法。 4. **多态性的体现**:父类引用指向子类对象时,通过父类引用调用重写的方法,实际执行的是子类重写的方法。这充分体现了继承与多态的结合。 ### 代码示例 ```java class Animal { public void makeSound() { System.out.println("动物发出声音"); } } class Dog extends Animal { // 重写父类的makeSound方法 @Override public void makeSound() { // 使用super调用父类的方法(可选) // super.makeSound(); // 如果这里调用,则先执行父类方法,再执行子类新增内容 System.out.println("汪汪叫"); } // 新增方法 public void wagTail() { System.out.println("摇尾巴"); } } public class Main { public static void main(String[] args) { // 创建子类对象 Dog myDog = new Dog(); myDog.makeSound(); // 调用子类重写的方法 // 父类引用指向子类对象 Animal animal = new Dog(); animal.makeSound(); // 多态:实际调用的是Dog类的makeSound方法 // 注意:父类引用无法调用子类新增的方法(如wagTail) // animal.wagTail(); // 编译错误 } } ``` ### 执行结果 ``` 汪汪叫 汪汪叫 ``` ### 说明 1. **继承的体现**: - `Dog`类通过`extends Animal`明确继承了`Animal`类。 - 在`Dog`类中,重写了`makeSound`方法,这是对父类方法的重写。 - 在`Dog`类的构造器中,如果没有显式调用父类构造器,编译器会自动插入`super()`调用父类的无参构造器。 2. **多态**: - 当父类引用`animal`指向子类对象`Dog`时,调用`makeSound`方法,实际执行的是子类重写的方法。这体现了运行时多态。 3. **重载与重写的区别**: - 如果子类中有一个方法与父类方法同名但参数列表不同,这是重载。此时,父类引用调用该方法时,仍然调用父类的方法(因为方法签名不同,没有覆盖)。只有重写(覆盖)才会导致多态调用。 ### 在父类构造器中调用重写方法 引用[1]中提到:在父类构造器中调用被子类重写的方法,会调用子类的方法。这是因为在创建子类对象时,子类的重写方法已经覆盖了父类的方法。但要注意,此时子类的初始化可能还未完成(即子类构造器中的代码还未执行),因此可能会引发问题。示例: ```java class Animal { public Animal() { makeSound(); // 在父类构造器中调用重写方法 } public void makeSound() { System.out.println("动物声音"); } } class Dog extends Animal { private String name = "小狗"; @Override public void makeSound() { System.out.println(name + "汪汪叫"); // 此时name可能还未初始化(为null) } } public class Main { public static void main(String[] args) { Dog dog = new Dog(); // 输出:null汪汪叫 } } ``` 在这个例子中,由于父类构造器在子类初始化之前运行,所以子类实例变量`name`还未被初始化(为默认值null)。因此,在父类构造器中调用可重写方法是不安全的。 ### 总结 在通过继承父类并重写方法的方式创建子类时,可以通过以下方式明确体现对父类继承: 1. 使用`extends`关键字声明子类继承父类。 2. 在子类中重写父类的方法,并遵循重写规则。 3. 使用`super`关键字显式调用父类的构造器或方法。 4. 利用多态性(父类引用指向子类对象)来调用子类重写的方法。 同时,要注意在父类构造器中避免调用可重写方法,以防止子类状态未完全初始化的问题。 --- ### §§相关问题§§ 1. 在Java中,子类继承父类时,构造器的调用顺序是怎样的? 2. 如何防止子类重写父类的方法? 3. 什么是协变返回类型?在方法重写中如何使用? 4. 为什么在父类构造器中调用可重写方法是不安全的?</think>### 理解继承与重写在Java中的体现 在Java中,通过继承父类并重写方法创建子类时,对父类继承主要体现在以下方面: #### 1. **显式继承声明** 子类必须使用`extends`关键字明确声明其父类: ```java class SubClass extends SuperClass { // 明确继承关系 // ... } ``` #### 2. **构造器链式调用** 子类构造器**必须**通过`super()`调用父类构造器(显式或隐式): ```java class Animal { public Animal() { System.out.println("父类构造器执行"); } } class Cat extends Animal { public Cat() { super(); // 显式调用父类构造器(编译器会自动添加) System.out.println("子类构造器执行"); } } ``` #### 3. **方法重写签名** 重写方法必须保持与父类**完全相同的方法签名**(方法名+参数列表),这是继承关系的直接体现: ```java class Shape { // 父类方法 public double calculateArea() { return 0.0; } } class Circle extends Shape { // 重写方法:保持相同签名 @Override public double calculateArea() { return Math.PI * radius * radius; } } ``` #### 4. **@Override注解** 使用`@Override`注解显式声明方法重写,编译器会验证是否符合继承规则: ```java class Bird extends Animal { @Override // 明确表示重写父类方法 public void makeSound() { System.out.println("啾啾"); } } ``` #### 5. **super关键字访问** 在子类中可通过`super`显式访问父类成员,体现继承层次: ```java class ElectricCar extends Car { @Override public void startEngine() { super.startEngine(); // 调用父类实现 System.out.println("电池系统启动"); } } ``` #### 6. **类型兼容性** 子类对象可赋值给父类引用,这是继承的核心体现: ```java Animal myPet = new Cat(); // 子类对象作为父类类型使用 myPet.makeSound(); // 实际调用Cat的makeSound() ``` #### 7. **继承树验证** 通过`instanceof`类型转换验证继承关系: ```java if(myPet instanceof Cat) { Cat cat = (Cat) myPet; // 向下转型 cat.climbTree(); } ``` ### 继承机制的核心原理 1. **方法调用动态绑定** 当通过父类引用调用方法时,JVM会根据**实际对象类型**动态绑定方法实现(虚方法表机制) 2. **内存结构体现继承** 子类对象内存包含: - 父类字段副本 - 子类字段副本 - 指向父类方法表的指针 - 指向子类方法表的指针 3. **初始化顺序保证** 对象初始化顺序严格遵循继承层次: ```mermaid graph LR A[父类静态块] --> B[子类静态块] B --> C[父类构造器] C --> D[子类构造器] ``` > 关键点:重写(Override)改变方法实现但保持接口不变,而重载(Overload)创建的是新方法签名,不影响继承关系[^1][^2]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值