按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
short 0
int 0
long 0
float 0。0
double 0。0
其中,Char 值为空(NULL ),没有数据打印出来。
稍后大家就会看到:在一个类的内部定义一个对象句柄时,如果不将其初始化成新对象,那个句柄就会获得
一个空值。
4。4。1 规定初始化
如果想自己为变量赋予一个初始值,又会发生什么情况呢?为达到这个目的,一个最直接的做法是在类内部
定义变量的同时也为其赋值(注意在C++里不能这样做,尽管C++的新手们总“想”这样做)。在下面,
Measurement 类内部的字段定义已发生了变化,提供了初始值:
109
…………………………………………………………Page 111……………………………………………………………
class Measurement {
boolean b = true;
char c = 'x';
byte B = 47;
short s = 0xff;
int i = 999;
long l = 1;
float f = 3。14f;
double d = 3。14159;
//。 。 。
亦可用相同的方法初始化非基本(主)类型的对象。若Depth 是一个类,那么可象下面这样插入一个变量并
进行初始化:
class Measurement {
Depth o = new Depth();
boolean b = true;
// 。 。 。
若尚未为o 指定一个初始值,同时不顾一切地提前试用它,就会得到一条运行期错误提示,告诉你产生了名
为“违例”(Exception)的一个错误(在第9 章详述)。
甚至可通过调用一个方法来提供初始值:
class CInit {
int i = f();
//。。。
}
当然,这个方法亦可使用自变量,但那些自变量不可是尚未初始化的其他类成员。因此,下面这样做是合法
的:
class CInit {
int i = f();
int j = g(i);
//。。。
}
但下面这样做是非法的:
class CInit {
int j = g(i);
int i = f();
//。。。
}
这正是编译器对“向前引用”感到不适应的一个地方,因为它与初始化的顺序有关,而不是与程序的编译方
式有关。
这种初始化方法非常简单和直观。它的一个限制是类型Measurement 的每个对象都会获得相同的初始化值。
有时,这正是我们希望的结果,但有时却需要盼望更大的灵活性。
110
…………………………………………………………Page 112……………………………………………………………
4。4。2 构建器初始化
可考虑用构建器执行初始化进程。这样便可在编程时获得更大的灵活程度,因为我们可以在运行期调用方法
和采取行动,从而“现场”决定初始化值。但要注意这样一件事情:不可妨碍自动初始化的进行,它在构建
器进入之前就会发生。因此,假如使用下述代码:
class Counter {
int i;
Counter() { i = 7; }
// 。 。 。
那么 i 首先会初始化成零,然后变成 7。对于所有基本类型以及对象句柄,这种情况都是成立的,其中包括
在定义时已进行了明确初始化的那些一些。考虑到这个原因,编译器不会试着强迫我们在构建器任何特定的
场所对元素进行初始化,或者在它们使用之前——初始化早已得到了保证(注释⑤)。
⑤:相反,C++有自己的“构建器初始模块列表”,能在进入构建器主体之前进行初始化,而且它对于对象来
说是强制进行的。参见《Thinking in C++》。
1。 初始化顺序
在一个类里,初始化的顺序是由变量在类内的定义顺序决定的。即使变量定义大量遍布于方法定义的中间,
那些变量仍会在调用任何方法之前得到初始化——甚至在构建器调用之前。例如:
//: OrderOfInitialization。java
// Demonstrates initialization order。
// When the constructor is called; to create a
// Tag object; you'll see a message:
class Tag {
Tag(int marker) {
System。out。println(〃Tag(〃 + marker + 〃)〃);
}
}
class Card {
Tag t1 = new Tag(1); // Before constructor
Card() {
// Indicate we're in the constructor:
System。out。println(〃Card()〃);
t3 = new Tag(33); // Re…initialize t3
}
Tag t2 = new Tag(2); // After constructor
void f() {
System。out。println(〃f()〃);
}
Tag t3 = new Tag(3); // At end
}
public class OrderOfInitialization {
public static void main(String'' args) {
Card t = new Card();
t。f(); // Shows that construction is done
}
111
…………………………………………………………Page 113……………………………………………………………
} ///:~
在Card 中,Tag 对象的定义故意到处散布,以证明它们全都会在构建器进入或者发生其他任何事情之前得到
初始化。除此之外,t3 在构建器内部得到了重新初始化。它的输入结果如下:
Tag(1)
Tag(2)
Tag(3)
Card()
Tag(33)
f()
因此,t3 句柄会被初始化两次,一次在构建器调用前,一次在调用期间(第一个对象会被丢弃,所以它后来
可被当作垃圾收掉)。从表面看,这样做似乎效率低下,但它能保证正确的初始化——若定义了一个过载的
构建器,它没有初始化 t3;同时在t3 的定义里并没有规定“默认”的初始化方式,那么会产生什么后果
呢?
2。 静态数据的初始化
若数据是静态的(static),那么同样的事情就会发生;如果它属于一个基本类型(主类型),而且未对其
初始化,就会自动获得自己的标准基本类型初始值;如果它是指向一个对象的句柄,那么除非新建一个对
象,并将句柄同它连接起来,否则就会得到一个空值(NULL )。
如果想在定义的同时进行初始化,采取的方法与非静态值表面看起来是相同的。但由于static 值只有一个存
储区域,所以无论创建多少个对象,都必然会遇到何时对那个存储区域进行初始化的问题。下面这个例子可
将这个问题说更清楚一些:
//: StaticInitialization。java
// Specifying initial values in a
// class definition。
class Bowl {
Bowl(int marker) {
System。out。println(〃Bowl(〃 + marker + 〃)〃);
}
void f(int marker) {
System。out。println(〃f(〃 + marker + 〃)〃);
}
}
class Table {
static Bowl b1 = new Bowl(1);
Table() {
System。out。println(〃Table()〃);
b2。f(1);
}
void f2(int marker) {
System。out。println(〃f2(〃 + marker + 〃)〃);
}
static Bowl b2 = new Bowl(2);
}
class Cupboard {
Bowl b3 = new Bowl(3);
112
…………………………………………………………Page 114……………………………………………………………
static Bowl b4 = new Bowl(4);
Cupboard() {
System。out。println(〃Cupboard()〃);