java继承中类的初始化过程

我们先看代码,然后再分析。

package se;

public class Beetle extends Insect{
    int k = prt("Beetle.k initialized");
    Beetle(){
        prt("k="+k);
        prt("j="+j);
    }
    static int x2=prt("static Beetle.x2 initialized");
    static{
        System.out.println("Beetle");
    }
    static int prt(String s){
        System.out.println(s);
        return 63;
    }
    public static void main(String[] args) {
        prt("Beetle constructor");
        Beetle b = new Beetle();
    }
}
class Insect{
    int i=9;
    int j;
    Insect(){
        j=prt("i="+i+",j="+j);
    }
    static{
        System.out.println("Insect");
    }
    static int xl = prt("static Insect.x1 initialized");
    static int prt(String s){
        System.out.println(s);
        return 47;
    }
}

输出:
Insect
static Insect.x1 initialized
static Beetle.x2 initialized
Beetle
Beetle constructor
i=9,j=0
Beetle.k initialized
k=63
j=47

java中类的class文件只有在初次使用时才会加载到jvm中。类的静态成员、静态代码块和静态方法会随着类的加载而加载,并且只加载一次。初次使用包含两种情况:

  1. 静态调用。就是“类名.静态成员”或“类名.静态方法”。

  2. 使用构造器。

上面的程序先从Beetle类的main方法形如执行(可以理解成jvm执行Beetle.main(),也就是静态调用),于是类加载器开始加载Beetle类的字节码文件。在此过程中,编译器发现了“extends”关键字,即Beetle类还继承了Insect类,于是接着加载Insect类。上面的例子包含静态初始化和对象初始化两个过程。首先是开始Insect类(父类)的静态初始化。

static{
    System.out.println("Insect");
}
static int xl = prt("static Insect.x1 initialized");

静态代码块和静态成员的执行顺序由书写顺序决定,所以开始输出:
Insect
static Insect.x1 initialized
于是父类(Insect)的静态初始化结束。紧接着,类加载器继续加载子类(Beetle)的静态初始化,输出:
static Beetle.x2 initialized
Beetle
现在父类和子类的静态初始化操作全部完成。以上步骤仅仅是jvm试图调用Beetle类的main方法的准备动作,并没有真正执行main方法中的代码,现在开始真正执行:

public static void main(String[] args) {
    prt("Beetle constructor");
    Beetle b = new Beetle();
}

第一行输出
Beetle constructor
这没有疑问,显然易见的事。第二行开始实例化Beetle类,即开始对象的初始化了。我们看看Beetle类的构造器:

Beetle(){
    prt("k="+k);
    prt("j="+j);
}

它等价于:

Beetle(){
    supper();
    k=prt("Beetle.k initialized");
    prt("k="+k);
    prt("j="+j);
}

也就是在构造Beetle对象时需要先构造父类(Insect)对象。于是jvm又去执行Insect类的构造器:

Insect(){
    j=prt("i="+i+",j="+j);
}

里面使用到了成员变量,其实成员变量的初始化也是放在构造器中的,也就是Insect类中对象成员属性定义等价于:

class Insect{
    int i;
    int j;
    Insect(){
        super();
        i=9;
        j=0;
        j=prt("i="+i+",j="+j);
    }
    //其他部分省略
}

这样,实例化Insect对象时会输出:
i=9,j=0
注意,j的值,因为静态方法虽然能被子类继承但不能被重写,所以j的值是47而不是63,Insect类对象初始化完成。然后jvm回到Beetle构造器继续运行,完成Insect类的对象初始化,依次输出:
k=63
j=47


类初始华过程总结:

  1. 父类静态初始化
  2. 子类静态初始化
  3. 父类对象初始化
  4. 子类对象初始化

其他总结:

  1. 当类第一次被静态调用或是通过构造器调用时会被加载到内存
  2. 对象的成员属性初始化是在构造器中完成的(紧跟在supper()语句后面)
  3. 静态方法可以被继承,但是不能被重写,因为静态方法属于类,而不是属于对象
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值