随着计算机的进步,“不安全”的程序设计已经成为造成编程代价高昂的罪魁祸首之一。
C++中引入了构造器的概念,在对象创建的过程中调用。Java中也沿用这种概念,但新增了自己的“垃圾收集器”,能在资源不再需要的时候自动释放他们。
用构造器自动初始化
Java中由于构造器的存在,可以确保每个类的对象都能正确的初始化。Java中构造器与类名相同,其原因有二:(1)我们使用的任何名字都有可能和打算作为类成员的名字相冲突;(2)由于编译器要调用构造器,所以它必须知道要调用的是哪个方法。构造器的名字跟类名相同就能很好地解决上面两个问题,这一点跟Delphi中用Create作为构造器的名字有异曲同工之妙。需要注意的是所有方法的第一个字母小写的原则并不适用于构造器,因为构造器的名字要跟类名相同。
Overload
人类的语言大多具有冗余性,一个名字可以表达多种意思。这个对应到软件行业就是程序的方法出现过载(overload),另外还有一个因素导致过载的出现,那就是构造器的名字是由类名决定的,因此只有一个构造器的名字,但是我们在调用构造器构造对象的时候可能有不同的要求,因此很容易需要对构造器进行过载。
区分过载方法其实很简单:每一个过载的方法都必须采用独一无二的参数类型列表。this关键字(只能在方法内部调用)可为已调用该方法的那个对象生成相应的句柄。this关键字只能用于那些特殊的类——需明确使用当前对象的句柄。若为一个类写了多个构建器,那么经常都需要在一个构建器里调用另一个构建器,以避免写重复的代码。可用this 关键字做到这一点。
//: Flower.java
// Calling constructors with "this"
public class Flower {
private int petalCount = 0;
private String s = new String("null");
Flower(int petals) {
petalCount = petals;
System.out.println(104 "Constructor w/ int arg only, petalCount = " + petalCount);
}
Flower(String ss) {
System.out.println("Constructor w/ String arg only, s=" + ss);
s = ss;
}
Flower(String s, int petals) {
this(petals);
//! this(s); // Can't call two!
this.s = s; // Another use of "this"
System.out.println("String & int args");
}
Flower() {
this("hi", 47);
System.out.println("default constructor (no args)");
}
void print() {
//! this(11); // Not inside non-constructor!
System.out.println("petalCount = " + petalCount + " s = "+ s);
}
public static void main(String[] args) {
Flower x = new Flower();
x.print();
}
}
从Flower(String s, int petals)中可以看出,尽管可以用this调用一个构造器,但是不可以调用两个。另外,构造器调用必须是我们做的第一件事,否则会收到编译程序的报错信息。
清除,收尾和垃圾收集
这一块还是理解的不好,以后再讨论。
成员初始化
在Java语言中,一个类的所有基本类型(主类型)数据成员都会保证获得一个初始值。但对于局部变量和非主类型变量却并不会默认赋初值。如果想自己为变量赋初值,直接的方法是在类内定义变量的时候也为其赋初值。如下图所示:
亦可用相同的方法初始化非主类型的对象。
上面的代码中i首先被初始化为0,正接着被赋值为7。在一个类中,变量的初始化顺序是由变量在类中定义的顺序决定的,即使变量定义夹杂在方法定义之间,也会在调用任何方法之前初始化变量—甚至在构造器调用之前。初始化的顺序首先是static对象,然后才是非static对象。
由此总结出一个对象的创建过程(以Dog类为例):
(1) 类型为Dog 的一个对象首次创建时,或者Dog 类的static 方法/static 字段首次访问时,Java 解释器必须找到Dog.class(在事先设好的类路径里搜索)。
(2) 找到Dog.class 后(它会创建一个Class 对象,这将在后面学到),它的所有static 初始化模块都会运行。因此,static 初始化仅发生一次——在Class 对象首次载入的时候。
(3) 创建一个new Dog()时,Dog 对象的构造进程首先会在内存堆(Heap)里为一个Dog 对象分配足够多的存储空间。
(4) 这种存储空间会清为零,将Dog 中的所有基本类型设为它们的默认值(零用于数字,以及boolean 和char 的等价设定)。
(5) 进行字段定义时发生的所有初始化都会执行。
(6) 执行构建器。正如第6 章将要讲到的那样,这实际可能要求进行相当多的操作,特别是在涉及继承的时候。