子类继承父类代码的执行顺序

本文详细解析了Java中父类与子类的加载顺序,包括静态与非静态代码块、构造函数及普通方法的执行流程。通过具体示例,帮助读者理解类加载机制。

新建两个类 分别为Fruit & Apple:

public class Fruit {

    public Fruit() {
        System.out.println("父类构造函数");
    }

    static {
        System.out.println("父类静态代码块");
    }

    public void eat(){
        System.out.println("父类普通方法");
    }

    {
        System.out.println("父类非静态代码块");
    }

}

class Apple extends Fruit{
    static {
        System.out.println("子类静态代码块");
    }

    public Apple() {
        System.out.println("子类构造函数");
    }

    {
        System.out.println("子类非静态代码块");
    }

    public void eat(){
        System.out.println("子类普通方法");
    }

}

执行以下代码:

public static void main(String[] args) {
        Fruit fruit = new Fruit();
        fruit.eat();
    }

new Fruit()会按照 父类静态块–子类静态块–父类非静态块–父类构造–父类普通方法 的顺序来执行。

out:
父类静态代码块
子类静态代码块
父类非静态代码块
父类构造函数
父类普通方法

new Apple()则以 父类静态块–子类静态块–父类非静态块–父类构造–子类非静态块–子类构造函数–子类普通方法 的顺序来执行。

out:
父类静态代码块
子类静态代码块
父类非静态代码块
父类构造函数
子类非静态代码块
子类构造函数
子类普通方法

由此可以得出 父类执行>子类执行 ,静态代码块>非静态代码块 , 构造函数>普通方法。

<think> 我们正在讨论Java中继承关系下的对象创建和方法调用问题。用户的问题集中在两个方面: 1. 子类如何调用父类方法来创建子类对象? 2. 父类如何调用方法来创建父类对象? 注意:用户提到“子类调用父类方法创建子类对象”和“父类调用方法创建父类对象”。我们需要正确理解这两个场景。 根据引用内容,我们已知: - 子类不会继承父类的构造方法(引用[1]和引用[3])。 - 在创建子类对象时,会先调用父类的构造方法(通过super(),显式或隐式)来初始化父类部分(引用[2]和引用[3])。 - 子类拥有父类的所有成员(包括私有成员,但不能直接访问,引用[3]和引用[4])。 现在,我们分别分析两个场景: 场景一:子类调用父类方法创建子类对象 这听起来像是子类通过父类的某个方法(非构造方法)来创建子类对象。这通常不是直接通过继承机制实现的。我们可以考虑以下可能性: a) 在父类中定义一个返回子类对象的静态工厂方法(但父类通常不知道子类的存在,除非使用抽象工厂模式)。 b) 在子类中覆盖父类的某个方法,使其返回子类对象(但这个方法不应该是构造方法,因为构造方法不能被继承也不能被覆盖)。 场景二:父类调用方法创建父类对象 这相对简单,父类中的方法(非静态)可以通过`new`关键字创建父类对象,但要注意避免循环创建。 然而,用户的问题可能被误解。另一种理解可能是:在创建对象时,构造方法的调用链(即子类构造方法调用父类构造方法,以及父类构造方法调用自身其他构造方法)。 基于引用[2]和引用[3],我们明确: - 创建子类对象时,会先调用父类构造方法(隐式或显式),然后再执行子类构造方法的剩余部分。 - 父类构造方法中不能创建子类对象(因为父类不知道子类的存在),但可以创建父类对象(比如在父类的一个非构造方法中返回new父类(),但这与构造链无关)。 因此,我们重新梳理用户问题: 用户可能想知道在继承关系中,创建对象时构造方法的调用顺序和机制。 根据引用,我们可以这样回答: 1. 子类对象创建时,子类构造方法会调用父类构造方法(通过super()),这是为了初始化从父类继承的成员。这并不意味着子类继承父类的构造方法,而是必须调用父类构造方法(引用[1][3])。 2. 父类对象创建时,直接调用父类的构造方法。 另外,用户可能还关心在父类中定义的方法能否返回子类对象?这可以通过工厂方法实现,但需要设计。 下面我们具体回答: 一、子类对象创建过程(涉及调用父类构造方法) 当创建子类对象时,构造方法的调用顺序: - 子类构造方法的第一行(如果没有显式调用super或this)会隐式调用父类无参构造方法。 - 如果父类没有无参构造方法,则子类必须显式调用父类的有参构造方法(使用super(参数))。 - 父类构造方法执行完毕后,再执行子类构造方法的剩余部分。 示例代码(基于引用[2]): ```java class A { public A() { System.out.println("A类构造方法"); } } class B extends A { public B() { // 这里会隐式调用super(),即父类A的无参构造方法 System.out.println("B类构造方法"); } } public class Main { public static void main(String[] args) { B b = new B(); // 输出:A类构造方法 B类构造方法 } } ``` 二、父类对象创建 父类对象创建直接使用父类的构造方法: ```java A a = new A(); // 调用A的构造方法 ``` 三、在父类方法中返回子类对象? 这通常不是直接通过继承机制实现的,但可以通过以下方式模拟: 1. 在父类中定义一个返回父类类型的方法,然后在子类中覆盖该方法并返回子类类型(协变返回类型)。 2. 使用工厂方法模式,在父类中定义一个静态方法,该方法可以返回子类对象(但父类需要知道子类,这通常不是好的设计)。 示例(协变返回类型): ```java class A { public A create() { return new A(); } } class B extends A { @Override public B create() { // 返回类型是B,子类 return new B(); } } public class Main { public static void main(String[] args) { A a = new A(); A a1 = a.create(); // 创建A对象 B b = new B(); B b1 = b.create(); // 创建B对象 } } ``` 注意:这里子类B覆盖了父类A的create方法,并返回B的实例。但是,这个方法是在子类对象上调用的,而不是在父类对象上调用后创建了子类对象。 四、在父类方法中创建父类对象 父类的普通方法中可以使用`new`来创建父类对象,例如: ```java class A { public A createA() { return new A(); // 在父类方法中创建父类对象 } } ``` 总结: - 子类对象创建必须通过子类的构造方法,而子类构造方法会调用父类构造方法(初始化父类部分)。 - 父类对象创建通过父类构造方法。 - 子类可以通过覆盖父类的方法(使用协变返回类型)来返回子类对象,但这并不是父类方法直接创建子类对象,而是子类对象的方法创建子类对象。 引用说明: [^1]: 子类不会继承父类的构造方法,但必须调用父类构造方法。 [^2]: 创建子类对象时会先调用父类构造方法,初始化父类部分。 [^3]: 子类对象包含父类的所有成员(包括私有成员,但私有成员不能直接访问)。 [^4]: 子类不会继承父类的static变量和static方法(实际上,static成员属于类,子类可以访问父类的static成员,但不是继承)。 相关问题: 1. 为什么子类构造方法需要调用父类构造方法? 2. 如何理解Java中的协变返回类型? 3. 在父类中创建子类对象的设计模式有哪些? 4. 子类对象创建过程中,内存分配是怎样的? 5. 如何避免在构造方法中调用可被覆盖的方法? </think>在 Java 继承体系中,对象创建和方法调用遵循严格的规则,以下是两种场景的实现原理和代码示例: ### 一、子类调用父类方法创建子类对象 当创建子类对象时,**必须隐式或显式调用父类构造方法**(通过`super()`),但最终创建的是子类对象本身[^1][^3]。 ```java class Parent { // 父类构造方法 public Parent() { System.out.println("父类构造方法执行"); } // 父类普通方法 public void parentMethod() { System.out.println("父类方法被调用"); } } class Child extends Parent { // 子类构造方法 public Child() { super(); // 隐式调用父类构造方法(可省略) System.out.println("子类构造方法执行"); } // 子类特有方法 public void childMethod() { System.out.println("子类方法被调用"); } } public class Main { public static void main(String[] args) { // 创建子类对象(自动调用父类构造方法) Child child = new Child(); /* 输出: 父类构造方法执行 子类构造方法执行 */ // 通过子类对象调用父类方法 child.parentMethod(); // 输出:"父类方法被调用" } } ``` **内存机制**: 1. 执行`new Child()`时,JVM 为子类对象分配内存 2. **先调用父类构造方法**初始化父类成员 3. 再调用子类构造方法初始化子类特有成员 4. 最终内存中包含完整的父类+子类结构[^3] ```mermaid classDiagram Parent <|-- Child class Parent { +Parent() +parentMethod() } class Child { +Child() +childMethod() } ``` ### 二、父类调用方法创建父类对象 父类独立创建对象时**不涉及子类**,直接通过自身构造方法实例化: ```java public class Main { public static void main(String[] args) { // 直接创建父类对象 Parent parent = new Parent(); // 输出:"父类构造方法执行" // 在父类方法中创建新父类对象 Parent newParent = parent.createParent(); newParent.parentMethod(); // 输出:"父类方法被调用" } } class Parent { ... // 工厂方法:返回新父类对象 public Parent createParent() { return new Parent(); // 调用自身构造方法 } } ``` ### 关键规则 1. **构造方法不被继承** 子类**不能直接继承**父类构造方法,必须通过`super()`调用[^1][^3] 2. **私有成员访问限制** 子类继承父类所有成员(包括`private`),但: - 可直接访问`public/protected`成员 - 需通过父类`getter/setter`访问`private`成员[^3][^4] ```java class Parent { private int id; // 私有属性 public int getId() { return id; } // 通过getter访问 } class Child extends Parent { void printId() { System.out.println(getId()); // 正确:通过父类方法访问 // System.out.println(id); // 错误:不能直接访问 } } ``` 3. **静态成员特殊规则** - `static`变量/方法**不被继承**,通过类名直接访问[^4] - 子类父类共享静态成员内存空间 ```java class Parent { static int count = 10; static void staticMethod() {} } class Child extends Parent { void accessStatic() { Parent.count = 20; // 通过父类名修改 Parent.staticMethod(); } } ``` ### 设计建议 1. **避免构造方法中调用可重写方法** 父类构造方法中调用重写方法会导致子类未初始化的状态错误 2. **优先使用`protected`而非`private`** 若需子类直接访问成员,使用`protected`修饰 3. **工厂方法替代直接构造** 通过静态工厂方法控制对象创建: ```java class Parent { public static Parent create() { return new Parent(); } } ``` > 💡 本质:子类对象包含完整的父类结构(内存拷贝),但构造过程必须遵循父类子类的初始化顺序[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值