首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

探究Java初始化的进程

2012-10-09 
探究Java初始化的过程最近又在翻《thinking in java》,这本书,怎么说呢,以前学java的时候,老师就没有把它作

探究Java初始化的过程

最近又在翻《thinking in java》,这本书,怎么说呢,以前学java的时候,老师就没有把它作为教材,但是我偏偏只买了这本书,一直收获很大。好了,言归正传,结合自己的偶然遇到的一个在构造函数中调多态方法引起的思考,讲述一下java的初始化到底是怎样的一个过程。

所谓初始化,当然也就指的是变量。变量可以是内置的变量或者我们创建的类的对象。

有人说,本来初始化本来就是一件很简单的事情,的确,但是java作为一门面向对象语言,由于具有继承、多态,静态、动态绑定等多种特性,所以其初始化的情景可谓是五花八门。现在就一步一步的分析其初始化过程。下面就是几个小原则。优先级依次递减。

1、静态块优先

程序首先会执行静态块的内容,这也就有了不写main方法就跑hello world的小故事,相信说到这里,大家就有了思路。我们都知道静态类型是和类绑定的而不是和具体实例对象绑定。也就是说,引用一个静态变量的方式往往是MyClass.xxx.这个特点决定了其在编译的阶段就已经分配好了固定的空间。

2、父类优先

由于继承的特性,当导出类(子类)的对象被创建的时候,程序将向上追溯到最初的父类,执行其初始化的操作。然后一次向下调用子类的构造函数。按照这个思路,那么每一个实例第一个要初始化的必定是Object类了。

3、成员变量优先

一定要注意,成员变量按照其声明的顺序会被初始化,并且立刻被初始化为二进制的0,这个动作发生在所有事件之前,也就是编译器会立刻将分配给对象的空间初始化。一会的小例子将证明这一点。

最后就是调用类的构造方法了。

下面有一个不错的例子,为了演示成员变量最早被初始化为0了,我们将在父类的构造函数中调用子类的方法(利用了多态)。

  1. package?fruit; ?
  2. ???
  3. ?import?vege.Inner; ?
  4. ???
  5. ???
  6. ??
  7. ?public?class?Fruit?{ ?
  8. ?????//static?block??
  9. ?????static?{ ?
  10. ?????????System.out.println("In?Fruit?static"); ?
  11. ?????} ?
  12. ???????
  13. ?????private?Inner?i?=?new?Inner();?//a?private?member??
  14. ?????public?Fruit(){ ?
  15. ?????????System.out.println("Before?Fruit?Constructor"); ?
  16. ?????????show();??//由于多态的特性,此处子类Apple覆写的方法会被调用??
  17. ?????????System.out.println("After?Fruit?Constructor"); ?
  18. ?????} ?
  19. ?????public?void?show(){ ?
  20. ?????????System.out.println("show:Fruit."); ?
  21. ?????} ?
  22. ???????
  23. ?????public?static?void?main(String[]?args)?{ ?
  24. ?????????//?TODO?code?application?logic?here??
  25. ?????????new?Apple(3); ?
  26. ?????} ?
  27. ??????????
  28. ?}?

现在父类中须要初始化的有

  • 静态块
  • 一个Inner类私有成员
  • 构造函数

    现在我们看子类的代码

    1. package?fruit; ?
    2. ???
    3. ?public?class?Apple?extends?Fruit{ ?
    4. ????//静态块??
    5. ?????static{ ?
    6. ?????????System.out.println("In?Apple?static"); ?
    7. ?????} ?
    8. ?????private?int?weight?=?1;?//初始化为1?注意区别这里和?初始化为0??
    9. ???????
    10. ?????public?Apple(int?para_weight){ ?
    11. ?????????System.out.println("Before?Apple?Constructer:?weight?=?"+weight); ?
    12. ?????????weight?=?para_weight; ?
    13. ?????????System.out.println("Apple?Constructor:?weight="+weight); ?
    14. ?????} ?
    15. ???????
    16. ?????@Override?
    17. ?????public?void?show(){ ?
    18. ?????????System.out.println("show?apple:?weight?="?+?weight); ?
    19. ?????} ?
    20. ???????
    21. ?}?
    子类须要初始化的有
    • 静态块
    • 私有成员weight
    • 构造函数

      那么当我们运行的时候会有怎样的结果呢?猜想。。。。。

      下面就是执行的结果:

      ?探究Java初始化的进程

      Look! 首先执行父类的静态块,之后是子类的静态块,这两个应该没有什么问题。接下来就是对父类成员变量的初始化了。首先是父类的私有成员Inner对象,打印了一条“ Inner Constructor”。

      接下来就是父类的构造函数,可见由于java的多态性,Fruit的构造方法调用了其子类Apple的show方法,并且我们可以清晰的看到,此刻Apple类中weight变量的值是0!说明,类的成员变量无论是否赋值,在各种初始化之前早已被设置为二进制0了。

      于是乎我想起了很多关于java的书都在说。。“如果类的私有变量没有赋值,就会被设置为0”。。这句话显然把时间弄混了。。。应该是编译器早已初始化了私有变量,均为0,之后才会执行到赋值语句。

      父类的构造函数结束之后,再次回到子类,初始化私有变量(也就是我们常说的赋值语句,因为初始为0的工作早做完了)。所以我们才会看到“Before Apple Constructor weight = 1”,执行完构造函数后,我们就看到了weight终于变成了我们创建对象是传进的3了,呼,初始化结束。

      总??结

      那么总结一下就是这样的:

      1. 编译器初始化所有的已分配的空间为二进制0 ?(这是我们的私有变量都会为0,刚才的例子)
      2. 执行父类静态代码 执行子类静态代码
      3. 初始化父类成员变量(我们常说的赋值语句)
      4. 初始化父类构造函数
      5. 初始化子类成员变量
      6. 初始化子类构造函数

      原文链接:http://www.cnblogs.com/octobershiner/archive/2012/03/12/2391899.html

热点排行