新手求问java类的加载顺序,初始化顺序,继承关系中构造器的调用等一系列问题,能否给我详细讲讲程序的执行过程。

Crazy_for_Messi 2014-04-12 10:40:58
最近自学java,看<think in java>到第7章了,很多东西感觉都已经明白,但在自己脑子里感觉全乱的,没有形成体系。我自己写了一个程序如下:
package reusing;
class Xx{
Xx() { System.out.println("Xx constructor"); }
}
class Zz{
Zz() { System.out.println("Zz constructor"); }
}
class Yy {
Xx x=new Xx();
static Zz zz=new Zz();
Yy() { System.out.println("Yy constructor"); }
}
class Aa extends Yy{
Aa(int i) { System.out.println("Aa constructor"); }
Xx xx=new Xx();
static Bb bb=new Bb();
}

class Bb {
Bb() { System.out.println("Bb constructor"); }
}

public class Cc extends Aa {
Bb b = new Bb();
Cc(){
super(1);
System.out.println("Cc constructor");
}
public static void main(String[] args) {
Cc c = new Cc();
}
}
结果为:
Zz constructor
Bb constructor
Xx constructor
Yy constructor
Xx constructor
Aa constructor
Bb constructor
Cc constructor


对于这段程序的完整执行流程我还是很不太明白的地方。我把自己理解的程序的执行过程描述一下,不懂和有错的地方希望大神们给我解惑一下,有点啰嗦,大家见谅。

1.首选程序要访问Cc.main方法,就必须加载Cc这个类(Think in java 上说类是在任何static 成员被访问时加载的)。
2.然后由关键字extends得知Cc类有一个父类Aa所以就加载Aa类
3.继续由Aa类发现父类Yy,然后加载Yy
4.然后看Yy类中有没有static变量需要初始化,没有,再看Aa中有没有,结果有static Bb b,所以进行初始化,这时候加载Bb类,并调用了构造函数,所以输出了第一条结果:Bb constructor
5.然后看Cc类有没有static 成员需要初试话,没有,所以开始执行main方法

(有点不太明白,书上说static变量是在类加载的时候初始化的,既然类加载的顺序是从导出类向上到基类,那为什么最开始初始化的还是父类的static 变量?这不是互相矛盾吗? 我自己的理解是,导出类还没加载完,就去加载它的父类了,意思是加载到Aa类,但还没加载到初始化static Bb bb的时候,就去加载Yy类了,然后把Yy类加载完,再继续回来加载Aa类,所以才先初始化父类的静态变量,不知道我这种解释对不对,请大神们给我讲解下)

6.执行Cc c = new Cc(); 语句创建一个Cc类的对象
从这里开始完全不明白了,为什么是先进行的最上面基类的初始化,再调用构造器,然后依次是导出类。导出类对父类构造器的显示调用感觉一点用都没有啊,super(1)那句话明明在Cc类的构造器中,可是Cc类中bb变量的初始化却又比构造器中的输入语句先执行,有点晕了,语言组织能力有点差,希望大家明白我的困惑,谁能给我解释下原理,还是说我自己理解的整个执行过程都是错的?
...全文
771 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
lliiqiang 2014-05-27
  • 打赏
  • 举报
回复
先初始化类,类初始化会调用父类初始化方法,然后才是调用构造函数,构造函数会调用父构造函数.
xuebichongkafei 2014-05-26
  • 打赏
  • 举报
回复
初始化顺序:先父类静态代码块--->再子类静态代码块-->默认初始化--->父类构造-->显示初始化-->构造代码块-->子类自身特有初始化 楼主记住这个顺序然后能就拿着分析就行了! 下面试着分析一下楼主的程序: 从子类开始,用到哪个加载哪个,有继承关系,先加载父类,然后加载子类,而静态是随着类的加载而加载的,所以静态成员会最先加载,而又因为A继承与Y,所以先加载完Y类,所以静态成员Z随着Y类最先加载完成,所以最先调用Z构造,而后加载A类,B静态成员随着A类加载而加载,所以紧接着调用了B构造,加载完类后,开始从父类一致往下执行构造,所以根据继承关系,C继承于A,A继承与Y,那么会先执行Y构造,但是执行Y自身构造方法之前,会先执行Y类中的成员显示初始化,那么,这时候第三个调用的就是X类构造了,执行完显示初始化后,就执行构造代码块,但是构造代码块没有,所以直接执行第四个构造,就是Y类自身构造了,整个Y类构造完之后回到A类,根据对Y类的分析,同理可以直到,先执行显示初始化,所以执行了X构造,然后就A构造,A类初始化完后就会执行C类自身的了,又同理,先执行了B类构造,然后再试C类自身的构造,所以你的代码构造函数执行的顺序是: Z B X Y X A B C 楼主明白了么? 还有,建议楼主现在没什么基础,不要去看thinking in java,个人觉得不适合初学者,由于外国人写的书,翻译过来,语法上的逻辑理解比较困,所以看起来可能会比较麻烦,建议先买一些简单粗浅的书看,或者下视频学更好啦
yuhouqingchen_2648 2014-05-25
  • 打赏
  • 举报
回复
建议你不要这么测试,每一个打印测试写的详细一点,存在下面这些个情况 父类 类变量 静态代码块 父类成员变量,子类 类变量 类静态代码块 子类成员变量 这几种优先级的关系
zzwab99 2014-04-13
  • 打赏
  • 举报
回复
关于第6点,当new一个类的对象时, 1. 首先为该类分配堆内存,该对内存空间会被清零,所以该类的基本类型成员会被设置成默认值,引用被设置为Null; 2. 其次,若该类的成员在定义时有初始化行为,如Cc类中的Bb b = new Bb();则会在调用自身构造函数之前进行这些成员的初始化,所以输出Bb constructor。 有问题可以再交流交流。
zzwab99 2014-04-13
  • 打赏
  • 举报
回复
1. 第4步分析有问题:Yy类有静态成员static Zz zz=new Zz();所以会初始化该成员,所以第一条输出是Zz constructor。 2. 加载一个类时,如果此类有父类,则会在进行static成员初始化操作之前,加载父类,因此初始化顺序是父类的static成员首先进行;之所以先加载父类再进行自身static成员的初始化,在于父类其实是子类的一部分(可以理解为子类的类文件=自身的类文件+父类的类文件),因此子类要加载完全自身的信息,就必须把父类的文件也加载进来,再进行自身的初始化;
Crazy_for_Messi 2014-04-13
  • 打赏
  • 举报
回复
引用 2 楼 zzwab99 的回复:
关于第6点,当new一个类的对象时, 1. 首先为该类分配堆内存,该对内存空间会被清零,所以该类的基本类型成员会被设置成默认值,引用被设置为Null; 2. 其次,若该类的成员在定义时有初始化行为,如Cc类中的Bb b = new Bb();则会在调用自身构造函数之前进行这些成员的初始化,所以输出Bb constructor。 有问题可以再交流交流。
当new一个对象的,是为这个类的对象(实例)分配内存空间对吧?如果类的成员在定义时有初始化行为,会在构造函数调用之前初始化成员,这点我能理解。 我不理解的就是super(i),这种显示调用究竟有什么用?这句话明明放在了到处类构造器中的第一句,那么必然是先要调用基类的构造器才能执行super(i)啊,但是这就和输出的结果矛盾了
Crazy_for_Messi 2014-04-13
  • 打赏
  • 举报
回复
引用 1 楼 zzwab99 的回复:
1. 第4步分析有问题:Yy类有静态成员static Zz zz=new Zz();所以会初始化该成员,所以第一条输出是Zz constructor。 2. 加载一个类时,如果此类有父类,则会在进行static成员初始化操作之前,加载父类,因此初始化顺序是父类的static成员首先进行;之所以先加载父类再进行自身static成员的初始化,在于父类其实是子类的一部分(可以理解为子类的类文件=自身的类文件+父类的类文件),因此子类要加载完全自身的信息,就必须把父类的文件也加载进来,再进行自身的初始化;
谢谢,你的意思我完全明白了。我本来在原来在Yy类中没有static Zz.后来加上去的,但是第4部这句话忘改了。

62,621

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧