随着计算机革命的发展,“不安全”的编程方式已逐渐成为编程代价高昂的主因之一。
初始化和清理(cleanup)正是涉及安全的两个问题。
许多C程序的错误都源于程序员忘记初始化变量。
特别是在使用程序库时,如果用户不知道如何初始化库的构件(或者是用户必须进行初始化的其他东西),更是如此。
清理也是一个特殊问题,当使用完一个元素时,它对你也就不会有什么影响了,所以很容易把它忘记。
这样一来,这个元素占用的资源就会一直得不到释放,结果是资源(尤其是内存)用尽。
C++引入了构造器(constructor)的概念,这是一个在创建对象时被自动调用的特殊方法。
Java中也采用了构造器,并额外提供了“垃圾回收器”。
对于不再使用的内存资源,垃圾回收器能自动将其释放。
本章将讨论初始化和清理的相关问题,以及Java对它们提供的支持。
Guaranteed initialization with the constructor
用构造器确保初始化
可以假想为编写的每个类都定义一个initialize()方法。该方法的名称提醒你在使用其对象之前,应首先调用initialize()。
然而,这同时意味着用户必须记得自己去调用此方法。
在Java中,通过提供构造器,类的设计者可确保每个对象都会得到初始化。
创建对象时,如果其类具有构造器,Java就会在用户有能力操作对象之前自动调用相应的构造器,从而保证了初始化的进行。
接下来的问题就是如何命名这个方法。有两个问题:第一,所取得任何名字都可能与类的某个成员名称相冲突;
第二,调用构造器是编译器的责任,所以必须让编译器直到应该调用哪个方法。
C++语言中采用的解决方法看来是最简单且更符合逻辑,所以在Java中也采用了这种方案:
即构造器采用与类相同的名称。
考虑到在初始化期间要自动调用构造器,这种做法就顺理成章了。
以下就是一个带有构造器的简单类:
//: initialization/SimpleConstructor.java
// Demonstration of a simple constructor.
class Rock {
Rock() { // This is the constructor
System.out.print("Rock ");
}
}
public class SimpleConstructor {
public static void main(String[] args) {
for(int i = 0; i < 10; i++)
new Rock();
}
} /* Output:
Rock Rock Rock Rock Rock Rock Rock Rock Rock Rock
*///:~
现在,在创建对象时:new Rock();
将会为对象分配存储空间,并调用相应的构造器。
这就确保了在你能操作对象之前,它已经被恰当的初始化了。
请注意,由于构造器的名称必须与类名完全相同,所以”每个方法首字母小写“的编码风格并不适用于构造器。
不接受任何参数的构造器叫做默认构造器,Java文档中通常使用术语无参构造器,但是默认构造器在Java出现之前已经使用许多年了,所以我仍然倾向于使用它。
但是和其他方法一样,构造器也能带有形式参数,以便制定如何创建对象。
对于上述例子稍加修改,即可是构造器接受一个参数:
//: initialization/SimpleConstructor2.java
// Constructors can have arguments.
class Rock2 {
Rock2(int i) {
System.out.print("Rock " + i + " ");
}
}
public class SimpleConstructor2 {
public static void main(String[] args) {
for(int i = 0; i < 8; i++)
new Rock2(i);
}
} /* Output:
Rock 0 Rock 1 Rock 2 Rock 3 Rock 4 Rock 5 Rock 6 Rock 7
*///:~
有了构造器形式参数,就可以在初始化对象时提供实际参数。
例如,假如类Tree有一个构造器,它接受一个整型变量来表示树的高度,就可以这样创建一个Tree对象:
Tree t=new Tree(12); //12-foot tree
如果Tree(int)是Tree类中唯一的构造器,那么编译器将不会允许你以任何方式创建Tree对象。
构造器有助于减少错误,并使代码更易于阅读。
从概念上讲,初始化与创建时彼此独立的,然而在上面的代码中,你却找不到对initialize()方法的明确使用。
在Java中,初始化和创建捆绑在一起,两者不能分离。
构造器是一种特殊类型的方法,因为它没有返回值。
这与返回值为空(void)明显不同。
对于空返回值,尽管方法本身不会自动返回什么,但仍可选择让它返回别的东西。
构造器则不会返回任何东西,你别无选择(new表达式确实返回了对新建对象的引用,但构造器本身并没有任何返回值)。
假如构造器具有返回值,并且允许人们自行选择返回类型,那么势必得让编译器知道该如何处理此返回值。