`
cantellow
  • 浏览: 842334 次
  • 性别: Icon_minigender_1
  • 来自: 草帽海贼团
社区版块
存档分类
最新评论

初始化及类的加载

    博客分类:
  • Java
阅读更多

学习Java说难也不难,说容易也不容易,一个HelloWorld看起来简单,但是你清楚java背后是怎样加载类和初始化的吗?那些深入Java虚拟机的原理暂且不说,但至少有些流程你还是有必要搞清楚的。

每个类都是一个编译单元,应用程序的入口就是编译单元的main函数,因为它是static的,可以直接由类名来调用,不要把main想得太神奇,它也就只是一个方法,可以在main函数里再调用别个类的main函数,main函数也可以抛异常等等。。但是操作系统在调用main函数之前也必须先查找这个类然后将其加载。我们这里就假设一般的情况,有个名为Dog的类:

---类的加载---

1.当首次创建类型为Dog的对象时(构造器可以看成静态方法),或者当Dog类的静态方法/静态字段首次被访问时,Java解释器必须查找类路径,以定位Dog.class文件。

2.如果Dog是一个导出类,那么编译器注意到它有一个基类(这是由关键字extends得知的),于是它继续进行加载基类(只是加载基类,这里还并没有初始化任何东西),不管你是否打算产生一个该基类的对象,这都要发生。如果该基类还有其自身的基类,那么第二个基类就会被加载,如此类推。直到它所有的基类都被加载。

3.接下来,根基类中的static初始化即会被执行,然后是下一个导出类的static被初始化,以此类推,这个过程中,有关静态初始化的所有动作都会执行,因此,静态初始化只在class对象首次加载的时候进行一次。

4.当Dog中的static也初始化后,那么就在堆上为Dog对象分配足够的存储空间,这一步和下面的步骤都是在要求创建对象的前提下才涉及的,有只加载类但是不创建此类对象的情况,比如当Dog类的静态方法/静态字段首次被访问时,Java就只加载类并初始化其中的静态字段,但是并不分配空间创建此类的对象。

---对象的实例化---

5.在给对象分配存储空间之后,这块存储空间首先被清零,即在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制的零,这就是为什么类的成员有默认值。

6.然后开始初始化基类的字段。

7.然后开始初始化基类的构造器。

8.最后是执行Dog类字段处的初始化动作。

9.然后是执行Dog的构造器。

成员变量初始化是在父类构造函数调用完后,在此之前,成员变量的值均是默认值。

在前三部一般都没有什么问题,但是后面一旦涉及构造器就很容易出现问题,因为构造器这个东西很特殊,这里简要说明一些要注意的地方:

在构造器中,如果为this添加了参数列表,那么就有了不同的含义,这将产生对符合此参数列表的某个构造器的明确调用,这样,就成了在构造器中调用构造器。而且一个构造器中只能调用另一个构造器,不能调用两个或以上。此外,必须将基类的构造器或this这样调用同类中其他构造器放在构造器的最起始处,否则,编译器会报错。(知道这是为什么吗?从概念上讲,“初始化”与“创建”是彼此独立的,但是在Java中,它们是被捆绑在一起的,这是为了安全。所以保证在被初始化之前不能有任何其他的操作。)

当涉及多态方法行为时,就更应该注意了,考虑下面一段代码:

abstract class Glyph

{

    abstract void draw();

    Glyph(){draw();}

}

class RoundGlyph extends Glyph

{

    private int radius = 1;

    RoundGlyph(int r){radius = r;}

    void draw(){System.out.println(radius);}

}

 

如果在main函数里调用:new RoundGlyph(5);

你以为draw()输出会是1吗?不是的,是0。因为在基类Glyph构造器中调用的 void draw()其实是RoundGlyph对象调用的,它调用的不是Glyph的draw()方法而是调用的RoundGlyph的draw()方法,而RoundGlyph的draw()方法它是把radius输出,而在这个时候,radius还没初始化。它只是在之前被置为0,所以输出的是0。有趣吧?涉及构造器还有很多有趣的东西,我就不一一列出了~~

分享到:
评论
2 楼 该用户名已经存在 2011-11-23  
C_J 写道
"它调用的不是Glyph的draw()方法而是调用的RoundGlyph的draw()方法"

那是因为draw()方法为虚拟的...

之所以是0,是因为语言的构造顺序问题这里的父类的construct优先与子类

比较赞同这个观点,不过关于draw()方法的问题感觉你们都没有说清楚!
1 楼 C_J 2009-11-07  
"它调用的不是Glyph的draw()方法而是调用的RoundGlyph的draw()方法"

那是因为draw()方法为虚拟的...

之所以是0,是因为语言的构造顺序问题这里的父类的construct优先与子类

相关推荐

Global site tag (gtag.js) - Google Analytics