2009-01-27

构造器(constructor)和垃圾收集器(garbage collector)

  要了解Java的对象在整个运行过程中的存在情况,就必须先了解堆(heap)和栈(stack),对Java来说,Java虚拟机启动后,就能够从操作系统中取得一块内存区域来运行Java程序。  对于Java来说,堆和栈的区别在于,Java的所有对象都存在于可垃圾回收的堆上,也就是之前JVM从系统中取得的内存区域。而Java程序的方法调用和局部变量则存在于栈上。这篇笔记假设你已经知道栈的工作形式。
  从栈的工作原理,我们可以分析出程序执行的情况:当一个对象被创建,那它就存在于堆上,JVM将为这个对象提供足以保存所有实例变量的内存空间(如果实例变量是primitive时就是该primitive需要的空间,如果是对象的引用时纯粹保存引用)。当调用了对象的方法时,该方法和方法的局部变量(也称栈变量)被压入栈顶,因此,不断调用方法栈顶就不断更新,根据栈的工作形式我们知道,某一时刻栈只有最顶层才能被访问,因此,局部变量在栈上和它所属的方法在同一层中,其作用范围就仅在该方法在栈顶的时候。
  理解了上面的内容,就不难理解构造器和垃圾回收器了。

构造器:
  目前为止我听说过在Java里唯一称为函数的就是一个类的构造函数了,那么接下来列出构造函数的逻辑特征:

  • 构造函数名和类名相同,access level可以是public,可以不指定,也可以是private。
  • 地球人都知道,如果我们不写构造函数,编译器也会自己创造一个。但是当你写了个有参数的构造函数时,编译器就不会帮你弄个无参数的出来了。
  • 构造函数签名的区别仅仅在于参数表的不同。
  • 一个类的实例变量尽管没有在构造函数里初始化,它也有默认值:0 0.0 false 和null。但是局部变量嘛,鬼才知道。。。

  接下来是构造函数运行时的特征,当然,这就又牵扯到了继承关系上了:假设一个类B继承自类A,类A继承自Object类。
那么,当类B被实例化时,调用了类B的构造函数,我们知道类B肯定包含了类A和类Object的部分,显然,这样就需要运行类A和Object类的构造函数来构造出它们的实例,那么,这几个类的关系是如何的呢?
先从程序上来说:假设类B是:
public class B extends A {
  int test;
  public B() {
    test = 11;  }
}

那么,编译器会自动把它当成
public class B extends A {
  int test;
  public B() {
    super();
    test = 11
  }
}

来编译。显然,B的构造函数先调用了父类A的构造函数。
  这种变化的规则是,如果你没有写,那编译器一定在构造函数的第一行补上,如果你写了,那你写的super()也只能存在于构造函数的第一行。显然,这个super()调用的是无参数版本的父类构造函数。根据我们上面说的栈的特征。结果就显然了,第一个被完成的构造函数就是Object类的构造函数,因此,被实例化的顺序显然就是Object→A→B了。显然这也符合逻辑性,毕竟,儿子不可能比老子早出生。
  另外,值得一提的是,一个类的构造函数也可以调用自己的其他构造函数,但同时也要满足两个要求,this()和super()只能同时存在一个并且this()也必须放在第一行。当然,这里的this()和super()指的就是自己或者父类的构造函数,因此可以是有参数的版本比如:
this(false);
super(11);

这样的。

最后就是垃圾收集器了:
  我们都知道,一个对象的所有引用丢失的话,那么它就会被垃圾收集器视为垃圾,也就不可用了。因为我们可以对唯一的引用赋null或者把唯一的引用指向其他对象,不过还需要注意的是,当对象的引用是局部变量,也就是在栈上的时候,随着这个引用的生命周期结束,那该对象也将自动被消费,所以有时离开栈必死的局部变量的问题啦。

没有评论: