友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
Java编程思想第4版[中文版](PDF格式)-第36部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
for(int i = 0; i 《 a2。length; i++)
a2'i'++;
for(int i = 0; i 《 a1。length; i++)
prt(〃a1'〃 + i + 〃' = 〃 + a1'i');
}
static void prt(String s) {
System。out。println(s);
}
} ///:~
大家看到a1 获得了一个初始值,而 a2 没有;a2 将在以后赋值——这种情况下是赋给另一个数组。
这里也出现了一些新东西:所有数组都有一个本质成员(无论它们是对象数组还是基本类型数组),可对其
进行查询——但不是改变,从而获知数组内包含了多少个元素。这个成员就是 length。与C 和 C++类似,由
116
…………………………………………………………Page 118……………………………………………………………
于Java 数组从元素 0 开始计数,所以能索引的最大元素编号是“length…1”。如超出边界,C 和C++会“默
默”地接受,并允许我们胡乱使用自己的内存,这正是许多程序错误的根源。然而,Java 可保留我们这受这
一问题的损害,方法是一旦超过边界,就生成一个运行期错误(即一个“违例”,这是第9 章的主题)。当
然,由于需要检查每个数组的访问,所以会消耗一定的时间和多余的代码量,而且没有办法把它关闭。这意
味着数组访问可能成为程序效率低下的重要原因——如果它们在关键的场合进行。但考虑到因特网访问的安
全,以及程序员的编程效率,Java 设计人员还是应该把它看作是值得的。
程序编写期间,如果不知道在自己的数组里需要多少元素,那么又该怎么办呢?此时,只需简单地用new 在
数组里创建元素。在这里,即使准备创建的是一个基本数据类型的数组,new 也能正常地工作(new 不会创建
非数组的基本类型):
//: ArrayNew。java
// Creating arrays with new。
import java。util。*;
public class ArrayNew {
static Random rand = new Random();
static int pRand(int mod) {
return Math。abs(rand。nextInt()) % mod + 1;
}
public static void main(String'' args) {
int'' a;
a = new int'pRand(20)';
prt(〃length of a = 〃 + a。length);
for(int i = 0; i 《 a。length; i++)
prt(〃a'〃 + i + 〃' = 〃 + a'i');
}
static void prt(String s) {
System。out。println(s);
}
} ///:~
由于数组的大小是随机决定的(使用早先定义的pRand()方法),所以非常明显,数组的创建实际是在运行
期间进行的。除此以外,从这个程序的输出中,大家可看到基本数据类型的数组元素会自动初始化成“空”
值(对于数值,空值就是零;对于 char,它是null ;而对于boolean,它却是false)。
当然,数组可能已在相同的语句中定义和初始化了,如下所示:
int'' a = new int'pRand(20)';
若操作的是一个非基本类型对象的数组,那么无论如何都要使用new。在这里,我们会再一次遇到句柄问
题,因为我们创建的是一个句柄数组。请大家观察封装器类型 Integer,它是一个类,而非基本数据类型:
//: ArrayClassObj。java
// Creating an array of non…primitive objects。
import java。util。*;
public class ArrayClassObj {
static Random rand = new Random();
static int pRand(int mod) {
return Math。abs (rand。nextInt()) % mod + 1;
}
public static void main(String'' args) {
Integer'' a = new Integer'pRand(20)';
prt(〃length of a = 〃 + a。length);
for(int i = 0; i 《 a。length; i++) {
117
…………………………………………………………Page 119……………………………………………………………
a'i' = new Integer(pRand(500));
prt(〃a'〃 + i + 〃' = 〃 + a'i');
}
}
static void prt(String s) {
System。out。println(s);
}
} ///:~
在这儿,甚至在new 调用后才开始创建数组:
Integer'' a = new Integer'pRand(20)';
它只是一个句柄数组,而且除非通过创建一个新的 Integer对象,从而初始化了对象句柄,否则初始化进程
不会结束:
a'i' = new Integer(pRand(500));
但若忘记创建对象,就会在运行期试图读取空数组位置时获得一个“违例”错误。
下面让我们看看打印语句中String 对象的构成情况。大家可看到指向 Integer 对象的句柄会自动转换,从而
产生一个String,它代表着位于对象内部的值。
亦可用花括号封闭列表来初始化对象数组。可采用两种形式,第一种是Java 1。0 允许的唯一形式。第二种
(等价)形式自Java 1。1 才开始提供支持:
//: ArrayInit。java
// Array initialization
public class ArrayInit {
public static void main(String'' args) {
Integer'' a = {
new Integer(1);
new Integer(2);
new Integer(3);
};
// Java 1。1 only:
Integer'' b = new Integer'' {
new Integer(1);
new Integer(2);
new Integer(3);
};
}
} ///:~
这种做法大多数时候都很有用,但限制也是最大的,因为数组的大小是在编译期间决定的。初始化列表的最
后一个逗号是可选的(这一特性使长列表的维护变得更加容易)。
数组初始化的第二种形式(Java 1。1 开始支持)提供了一种更简便的语法,可创建和调用方法,获得与C 的
“变量参数列表”(C 通常把它简称为“变参表”)一致的效果。这些效果包括未知的参数(自变量)数量
以及未知的类型(如果这样选择的话)。由于所有类最终都是从通用的根类Object 中继承的,所以能创建一
个方法,令其获取一个 Object 数组,并象下面这样调用它:
//: VarArgs。java
// Using the Java 1。1 array syntax to create
// variable argument lists
class A { int i; }
118
…………………………………………………………Page 120……………………………………………………………
public class VarArgs {
static void f(Object'' x) {
for(int i = 0; i 《 x。length; i++)
System。out。println(x'i');
}
public static void main(String'' args) {
f(new Object'' {
new Integer(47); new VarArgs();
new Float(3。14); new Double(11。11) });
f(new Object'' {〃one〃; 〃two〃; 〃three〃 });
f(new Object'' {new A(); new A(); new A()});
}
} ///:~
此时,我们对这些未知的对象并不能采取太多的操作,而且这个程序利用自动String 转换对每个 Object 做
一些有用的事情。在第 11章(运行期类型标识或 RTTI ),大家还会学习如何调查这类对象的准确类型,使
自己能对它们做一些有趣的事情。
4。5。1 多维数组
在Java 里可以方便地创建多维数组:
//: MultiDimArray。java
// Creating multidimensional arrays。
import java。util。*;
public class MultiDimArray {
static Random rand = new Random();
static int pRand(int mod) {
return Math。abs(rand。nextInt()) % mod + 1;
}
public static void main(String'' args) {
int'''' a1 = {
{ 1; 2; 3; };
{ 4; 5; 6; };
};
for(int i = 0; i 《 a1。length; i++)
for(int j = 0; j 《 a1'i'。length; j++)
prt(〃a1'〃 + i + 〃''〃 + j +
〃' = 〃 + a1'i''j');
// 3…D array with fixed length:
int'''''' a2 = new int'2''2''4';
for(int i = 0; i 《 a2。length; i++)
for(int j = 0 ; j 《 a2'i'。length; j++)
for(int k = 0; k 《 a2'i''j'。length;
k++)
prt(〃a2'〃 + i + 〃''〃 +
j + 〃''〃 + k +
〃' = 〃 + a2'i''j''k');
// 3…D array with varied…length vectors:
int'''''' a3 = new int'pRand(7)''''';
for(int i = 0; i 《 a3。length; i++) {
119
…………………………………………………………Page 121……………………………………………………………
a3'i' = new int'pRand(5)''';
for(int j = 0; j 《 a3'i'。length; j++)
a3'i''j' = new int'pRand(5)';
}
for(int i = 0; i 《 a3。length; i++)
for(int j = 0; j 《 a3'i'。length ; j++)
for(int k = 0; k 《 a3'i''j'。length;
k++)
prt(〃a3'〃 + i + 〃''〃 +
j + 〃''〃 + k +
〃' = 〃 + a3'i''j''k');
// Array of non…primitive objects:
Integer'''' a4 = {
{ new Integer(1); new Integer(2)};
{ new Integer(3); new Integer(4)};
{ new Integer(5); new Integer(6)};
};
for(int i = 0; i 《 a4。length; i++)
for(int j = 0; j 《 a4'i'。length; j++)
prt(〃a4'〃 + i + 〃''〃 + j +
〃' = 〃 + a4'i''j');
Integer'''' a5;
a5 = new Integer'3''';
for(int i = 0; i 《 a5。length; i++) {
a5'i' = new Integer'3';
for(int j = 0; j 《 a5'i'。length; j++)
a5'i''j' = new Integer(i*j);
}
for(int i = 0; i 《 a5。length; i++)
for(int j = 0; j 《 a5'i'。length; j++)
prt(〃a5'〃 + i + 〃''〃 + j +
〃' = 〃 + a5'i''j');
}
static void prt(String s) {
System。out。println(s);
}
} ///:~
用于打印的代码里使用了 length,所以它不必依赖固定的数组大小。
第一个例子展示了基本数据类型的一个多维数组。我们可用花括号定出数组内每个矢量的边界:
int'''' a1 = {
{ 1; 2; 3; };
{ 4; 5; 6; };
};
每个方括号对都将我们移至数组的下一级。
第二个例子展示了用new 分配的一个三维数组。在这里,整个数组都是立即分配的:
int'''''' a2 = new int'2''2''4';
但第三个例子却向大家揭示出构成矩阵的每个矢量都可以有任意的长度:
int'''''' a3 = new int'pRand(7)''''';
120
…………………………………………………………Page 122……………………………………………………………
for(int i = 0; i 《 a3。length; i++) {
a3'i' = new int'pRand(5)''';
for(int j = 0; j 《 a3'i'。length; j++)
a3'i''j' = new int'pRand(5)';
}
对于第一个 new 创建的数组,它的第一个元素的长度是随机的,其他元素的长度则没有定义。for 循环内的
第二个 new 则会填写元素,但保持第三个索引的未定状态——直到碰到第三个new。
根据输出结果,大家可以看到:假若没有明确指定初始化值,数组值就会自动初始化成零。
可用类似的表式处理非基本类型对象的数组。这从第四个例子可以看出,它向我们演示了用花括号收集多个
new 表达式的能力:
Integer'''' a4 = {
{ new Integer(1); new Integer(2)};
{ new Integer(3); new Integer(4)};
{ new Integer(5); new Integer(6)};
};
第五个例子展示了如何逐渐构建非基本类型的对象数组:
Integer'''' a5;
a5 = new Integer'3''';
for(int i = 0; i 《 a5。length; i++) {
a5'i' = new Integer'3';
for(int j = 0; j 《 a5'i'。length; j++)
a5'i''j' = new Integer(i*j);
}
i*j 只是在 Integer里置了一个有趣的值。
4。6 总结
作为初始化的一种具体操作形式,构建器应使大家明确感受到在语言中进行初始化的重要性。与 C++的程序
设计一样,判断一个程序效率如何,关键是看是否由于变量的初始化不正确而造成了严重的编程错误(臭
虫)。这些形式的错误很难发现,而且类似的问题也适用于不正确的清除或收尾工作。由于构建器使我们能
保证正确的初始化和清除(若没有正确的构建器调用,编译器不允许对象创建),所以能获得完全的控制权
和安全性。
在C++中,与“构建”相反的“破坏”(Destruction)工作也是相当重要的,因为用new 创建的对象必须明
确地清除。在Java 中,垃圾收集器会自动为所有对象释放内存,所以 Java 中等价的清除方法并不是经常都
需要用到的。如果不需要类似于构建器的行为,Java 的垃圾收集器可以极大简化编程工作,而且在内存的管
理过程中增加更大的安全性。有些垃圾收集器甚至能清除其他资源,比如图形和文件句柄等。然而
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!