java基础:继承关系类加载顺序问题

本文详细解析了Java中构造器的执行顺序、静态代码块的执行时机以及子类如何调用父类构造方法等问题,并通过具体示例展示了不同情况下构造器的调用流程。

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

/**
 * Created by fei on 2017/5/31.
 */
public class SonClass extends ParentClass{
    public SonClass(){
        System.out.println("SonClass's constructor");
    }
    { System.out.println("SonClass's block");}
    static {
        System.out.println("SonClass's static block ");
    }

    public static void main(String[] args) {
        System.out.println("------ main start ------ ");
        new SonClass();
        System.out.println("------ main  end  ------ ");
    }
}

class ParentClass{
    public ParentClass(){
        System.out.println("ParentClass's constructor");
    }
    { System.out.println("ParentClass's block");}
    static {
        System.out.println("ParentClass's static block ");
    }
}

运行结果:

ParentClass's static block 
SonClass's static block 
------ main start ------ 
ParentClass's block
ParentClass's constructor
SonClass's block
SonClass's constructor
------ main  end  ------ 

根据运行结果,一目了然,在执行 main 方法中 new SonClass() 之前,就在类加载之后执行了类中 static 代码块。然后再进入main方法,执行new操作,当然显而易见,在执行new子类操作的时候,是要先进行其父类的构造,即先执行父类的构造代码块(代码中只用大括号包裹的那段代码)以及构造函数 ,然后再执行子类的构造代码块以及构造函数。

修改一下代码,再来看看运行的结果:

/**
 * Created by fei on 2017/5/31.
 */
public class SonClass extends ParentClass{
    ParentClass parentClass;
    public SonClass(){
        System.out.println("1");
    }
    public SonClass(String name){
        System.out.println("2");
        this.name = name;
        parentClass = new ParentClass("FEI");
    }

    public static void main(String[] args) {
        System.out.println("------ main start ------ ");
        new SonClass("fei");
        System.out.println("------ main  end  ------ ");
    }
}

class ParentClass{
    String name ;
    public ParentClass(){
        System.out.println("3");
    }
    public ParentClass(String name){
        System.out.println("4");
        this.name = name ;
    }
}

运行的顺序是:

------ main start ------ 
3
2
4
------ main  end  ------ 
  • 第一个规则:子类的构造过程中,必须调用其父类的构造方法。一个类,如果我们不写构造方法,那么编译器会帮我们加上一个默认的构造方法(就是没有参数的构造方法),但是如果你自己写了构造方法,那么编译器就不会给你添加了,所以有时候当你new一个子类对象的时候,肯定调用了子类的构造方法,但是如果在子类构造方法中我们并没有显示的调用基类的构造方法,如:super(); 这样就会调用父类没有参数的构造方法。

  • 第二个规则:如果子类的构造方法中既没有显示的调用基类构造方法,而基类中又没有无参的构造方法,则编译出错,所以,通常我们需要显示的:super(参数列表),来调用父类有参数的构造函数,此时无参的构造函数就不会被调用。

总之,一句话:子类没有显示调用父类构造函数,不管子类构造函数是否带参数都默认调用父类无参的构造函数,若父类没有则编译出错。

还是两个类,我们再更改一下。

/**
 * Created by fei on 2017/5/31.
 */
public class SonClass extends ParentClass{
    private String name = "SonClass";

    public SonClass() {
        printName();
    }
    public void printName() {
        System.out.println("SonClass print name: " + name);
    }
    public static void main(String[] args){
        new SonClass();
    }
}

class ParentClass{
    private String name = "ParentClass";

    public ParentClass() {
        //System.out.println(this.getClass());
        printName();
    }
    public void printName() {
        System.out.println("ParentClass print name: " + name);
    }
}

看了上面的两个例子,最后这个例子就很容易被迷惑,可能有人会觉得运行结果是类似这样的:

ParentClass print name: ParentClass 
SonClass print name: SonClass

或者是:

ParentClass print name: SonClass
SonClass print name: SonClass

但真正的结果是这样的:

SonClass print name: null
SonClass print name: SonClass

为什么会这样,其实只要打开代码中父类构造器中的这句注释,就很容易理解了:System.out.println(this.getClass())
结果是:

class SonClass

没错,父类中的this引用是子类实例对象,所以在父类构造函数里调用的还是子类的printName()方法。具体原因也并我能十分肯定,我个人浅见,是因为虽然我们调用了父类的构造方法,但是我们并没有实例化出父类的实例对象,所以this还是指向的是子类的引用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值