友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
富士康小说网 返回本书目录 加入书签 我的书架 我的书签 TXT全本下载 『收藏到我的浏览器』

Java编程思想第4版[中文版](PDF格式)-第16部分

快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!



展,这个速度有一定程度的提高,但和C 比起来仍然很悬殊。计算机最注重的就是速度;假如在一台计算机 

上不能明显较快地干活,那么还不如用手做(有人建议在开发期间使用Java,以缩短开发时间。然后用一个 

工具和支撑库将代码转换成C++,这样可获得更快的执行速度)。  

为使Java 适用于大多数Web 开发项目,关键在于速度上的改善。此时要用到人们称为“刚好及时”(Just

In Time,或JIT)的编译器,甚至考虑更低级的代码编译器(写作本书时,也有两款问世)。当然,低级代 

码编译器会使编译好的程序不能跨平台执行,但同时也带来了速度上的提升。这个速度甚至接近 C 和 C++。 

而且Java 中的程序交叉编译应当比C 和 C++中简单得多(理论上只需重编译即可,但实际仍较难实现;其他 

语言也曾作出类似的保证)。  

在本书附录,大家可找到与Java /C++比较.对Java 现状的观察以及编码规则有关的内容。  



                                                              45 


…………………………………………………………Page 47……………………………………………………………

                      第 2 章  一切都是对象  



 “尽管以C++为基础,但 Java 是一种更纯粹的面向对象程序设计语言”。  

无论C++还是Java 都属于杂合语言。但在 Java 中,设计者觉得这种杂合并不象在 C++里那么重要。杂合语言 

允许采用多种编程风格;之所以说 C++是一种杂合语言,是因为它支持与 C 语言的向后兼容能力。由于C++是 

C 的一个超集,所以包含的许多特性都是后者不具备的,这些特性使 C++在某些地方显得过于复杂。  

Java 语言首先便假定了我们只希望进行面向对象的程序设计。也就是说,正式用它设计之前,必须先将自己 

的思想转入一个面向对象的世界(除非早已习惯了这个世界的思维方式)。只有做好这个准备工作,与其他 

OOP 语言相比,才能体会到 Java 的易学易用。在本章,我们将探讨 Java 程序的基本组件,并体会为什么说 

Java 乃至Java 程序内的一切都是对象。  



2。1 用句柄操纵对象  



每种编程语言都有自己的数据处理方式。有些时候,程序员必须时刻留意准备处理的是什么类型。您曾利用 

一些特殊语法直接操作过对象,或处理过一些间接表示的对象吗(C 或C++里的指针)?  

所有这些在 Java 里都得到了简化,任何东西都可看作对象。因此,我们可采用一种统一的语法,任何地方均 

可照搬不误。但要注意,尽管将一切都“看作”对象,但操纵的标识符实际是指向一个对象的“句柄” 

 (Handle)。在其他Java 参考书里,还可看到有的人将其称作一个“引用”,甚至一个“指针”。可将这一 

情形想象成用遥控板(句柄)操纵电视机(对象)。只要握住这个遥控板,就相当于掌握了与电视机连接的 

通道。但一旦需要“换频道”或者“关小声音”,我们实际操纵的是遥控板(句柄),再由遥控板自己操纵 

电视机(对象)。如果要在房间里四处走走,并想保持对电视机的控制,那么手上拿着的是遥控板,而非电 

视机。  

此外,即使没有电视机,遥控板亦可独立存在。也就是说,只是由于拥有一个句柄,并不表示必须有一个对 

象同它连接。所以如果想容纳一个词或句子,可创建一个 String 句柄:  

String s;  

但这里创建的只是句柄,并不是对象。若此时向 s 发送一条消息,就会获得一个错误(运行期)。这是由于 

s 实际并未与任何东西连接(即“没有电视机”)。因此,一种更安全的做法是:创建一个句柄时,记住无 

论如何都进行初始化:  

String s = 〃asdf〃;  

然而,这里采用的是一种特殊类型:字串可用加引号的文字初始化。通常,必须为对象使用一种更通用的初 

始化类型。  



2。2 所有对象都必须创建  



创建句柄时,我们希望它同一个新对象连接。通常用new 关键字达到这一目的。new 的意思是:“把我变成 

这些对象的一种新类型”。所以在上面的例子中,可以说:  

String s = new String(〃asdf〃);  

它不仅指出“将我变成一个新字串”,也通过提供一个初始字串,指出了“如何生成这个新字串”。  

当然,字串(String)并非唯一的类型。Java 配套提供了数量众多的现成类型。对我们来讲,最重要的就是 

记住能自行创建类型。事实上,这应是Java 程序设计的一项基本操作,是继续本书后余部分学习的基础。  



2。2。1  保存到什么地方  



程序运行时,我们最好对数据保存到什么地方做到心中有数。特别要注意的是内存的分配。有六个地方都可 

以保存数据:  

(1) 寄存器。这是最快的保存区域,因为它位于和其他所有保存方式不同的地方:处理器内部。然而,寄存 

器的数量十分有限,所以寄存器是根据需要由编译器分配。我们对此没有直接的控制权,也不可能在自己的 

程序里找到寄存器存在的任何踪迹。  

(2) 堆栈。驻留于常规 RAM (随机访问存储器)区域,但可通过它的“堆栈指针”获得处理的直接支持。堆 

栈指针若向下移,会创建新的内存;若向上移,则会释放那些内存。这是一种特别快、特别有效的数据保存 

方式,仅次于寄存器。创建程序时,Java 编译器必须准确地知道堆栈内保存的所有数据的“长度”以及“存 

在时间”。这是由于它必须生成相应的代码,以便向上和向下移动指针。这一限制无疑影响了程序的灵活 

性,所以尽管有些 Java 数据要保存在堆栈里——特别是对象句柄,但Java 对象并不放到其中。  



                                                               46 


…………………………………………………………Page 48……………………………………………………………

(3) 堆。一种常规用途的内存池(也在 RAM 区域),其中保存了Java 对象。和堆栈不同,“内存堆”或 

 “堆”(Heap )最吸引人的地方在于编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要 

在堆里停留多长的时间。因此,用堆保存数据时会得到更大的灵活性。要求创建一个对象时,只需用new 命 

令编制相关的代码即可。执行这些代码时,会在堆里自动进行数据的保存。当然,为达到这种灵活性,必然 

会付出一定的代价:在堆里分配存储空间时会花掉更长的时间!  

(4) 静态存储。这儿的“静态”(Static)是指“位于固定位置”(尽管也在RAM 里)。程序运行期间,静 

态存储的数据将随时等候调用。可用 static 关键字指出一个对象的特定元素是静态的。但 Java 对象本身永 

远都不会置入静态存储空间。  

(5) 常数存储。常数值通常直接置于程序代码内部。这样做是安全的,因为它们永远都不会改变。有的常数 

需要严格地保护,所以可考虑将它们置入只读存储器(ROM)。  

(6) 非RAM 存储。若数据完全独立于一个程序之外,则程序不运行时仍可存在,并在程序的控制范围之外。 

其中两个最主要的例子便是“流式对象”和“固定对象”。对于流式对象,对象会变成字节流,通常会发给 

另一台机器。而对于固定对象,对象保存在磁盘中。即使程序中止运行,它们仍可保持自己的状态不变。对 

于这些类型的数据存储,一个特别有用的技巧就是它们能存在于其他媒体中。一旦需要,甚至能将它们恢复 

成普通的、基于RAM 的对象。Java 1。1 提供了对Lightweight persistence 的支持。未来的版本甚至可能提 

供更完整的方案。  



2。2。2  特殊情况:主要类型  



有一系列类需特别对待;可将它们想象成“基本”、“主要”或者“主”(Primitive)类型,进行程序设计 

时要频繁用到它们。之所以要特别对待,是由于用 new 创建对象(特别是小的、简单的变量)并不是非常有 

效,因为new 将对象置于“堆”里。对于这些类型,Java 采纳了与 C 和 C++相同的方法。也就是说,不是用 

new 创建变量,而是创建一个并非句柄的“自动”变量。这个变量容纳了具体的值,并置于堆栈中,能够更 

高效地存取。  

Java 决定了每种主要类型的大小。就象在大多数语言里那样,这些大小并不随着机器结构的变化而变化。这 

种大小的不可更改正是 Java 程序具有很强移植能力的原因之一。  

  

主类型 大小 最小值 最大值 封装器类型  

  

boolean 1 位 Boolean  

char 16位 Unicode 0 Unicode 2 的 16次方…1 Character  

byte 8 位 …128 +127 Byte (注释①)  

short 16 位 …2 的15 次方 +2 的 15次方…1 Short (注释①)  

int 32位 …2 的 31 次方 +2 的31 次方…1 Integer  

long 64位 …2 的63 次方 +2 的63 次方…1 Long  

float 32 位 IEEE754 IEEE754 Float  

double 64 位 IEEE754 IEEE754 Double  

Void Void (注释①)  

  

①:到 Java 1。1 才有,1。0 版没有。  

  

数值类型全都是有符号(正负号)的,所以不必费劲寻找没有符号的类型。  

主数据类型也拥有自己的“封装器”(wrapper)类。这意味着假如想让堆内一个非主要对象表示那个主类 

型,就要使用对应的封装器。例如:  

char c = 'x';  

Character C = new Character('c');  

也可以直接使用:  

Character C = new Character('x');  

这样做的原因将在以后的章节里解释。  

  

1。 高精度数字  

Java 1。1 增加了两个类,用于进行高精度的计算:BigInteger 和 BigDecimal。尽管它们大致可以划分为 

 “封装器”类型,但两者都没有对应的“主类型”。  



                                                                            47 


…………………………………………………………Page 49……………………………………………………………

这两个类都有自己特殊的“方法”,对应于我们针对主类型执行的操作。也就是说,能对 int或 float做的 

事情,对BigInteger 和BigDecimal 一样可以做。只是必须使用方法调用,不能使用运算符。此外,由于牵 

涉更多,所以运算速度会慢一些。我们牺牲了速度,但换来了精度。  

BigInteger 支持任意精度的整数。也就是说,我们可精确表示任意大小的整数值,同时在运算过程中不会丢 

失任何信息。  

BigDecimal 支持任意精度的定点数字。例如,可用它进行精确的币值计算。  

至于调用这两个类时可选用的构建器和方法,请自行参考联机帮助文档。  



2。2。3 Java 的数组  



几乎所有程序设计语言都支持数组。在C 和 C++里使用数组是非常危险的,因为那些数组只是内存块。若程 

序访问自己内存块以外的数组,或者在初始化之前使用内存(属于常规编程错误),会产生不可预测的后果 

 (注释②)。  

  

②:在 C++里,应尽量不要使用数组,换用标准模板库(Standard TemplateLibrary)里更安全的容器。  

  

Java 的一项主要设计目标就是安全性。所以在C 和 C++里困扰程序员的许多问题都未在Java 里重复。一个 

Java 可以保证被初始化,而且不可在它的范围之外访问。由于系统自动进行范围检查,所以必然要付出一些 

代价:针对每个数组,以及在运行期间对索引的校验,都会造成少量的内存开销。但由此换回的是更高的安 

全性,以及更高的工作效率。为此付出少许代价是值得的。  

创建对象数组时,实际创建的是一个句柄数组。而且每个句柄都会自动初始化成一个特殊值,并带有自己的 

关键字:null (空)。一旦Java 看到null,就知道该句柄并未指向一个对象。正式使用前,必须为每个句 

柄都分配一个对象。若试图使用依然为null 的一个句柄,就会在运行期报告问题。因此,典型的数组错误在 

Java 里就得到了避免。  

也可以创建主类型数组。同样地,编译器能够担保对它的初始化,因为会将那个数组的内存划分成零。  

数组问题将在以后的章节里详细讨论。  



2。3 绝对不要清除对象  



在大多数程序设计语言中,变量的“存在时间”(Lifetime )一直是程序员需要着重考虑的问题。变量应持 

续多长的时间?如果想清除它,那么何时进行?在变量存在时间上纠缠不清会造成大量的程序错误。在下面 

的小节里,将阐示Java 如何帮助我们完成所有清除工作,从而极大了简化了这个问题。  



2。3。1  作用域  



大多数程序设计语言都提供了“作用域”(Scope)的概念。对于在作用域里定义的名字,作用域同时决定了 

它的“可见性”以及“存在时间”。在C,C++和 Java 里,作用域是由花括号的位置决定的。参考下面这个 

例子:  

  

{  

  int x = 12;  

  /* only x available */  

  {  

    int q = 96;  

    /* both x & q available */  

  }  

  /* only x available */  

  /* q “out of scope” */  

}  

  

作为在作用域里定义的一个变量,它只有在那个作用域结束之前才可使用。  

在上面的例子中,缩进排版使 Java 代码更易辨读。由于 Java 是一种形式自由的语言,所以额外的空格、制 

表位以及回车都不会对结果程序造成影响。  

注意尽管在 C 和 C++里是合法的,但在 Java 里不能象下面这样书写代码:  



                                                                            48 


…………………………………………………………Page 50……………………………………………………………

  

{  

  int x = 12;  

  {  

    int x = 96; /* illegal */  

  }  

}  

  

编译器会认为变量 x 已被定义。所以C 和C++能将一个变量“隐藏”在一个更大的作用域里。但这种做法在 

Java 里是不允许的,因为Java 的设计者认为这样做使程序产生了混淆。  



2。3。2  对象的作用域  



Java 对象不具备与主类型一样的存在时间。用 new 关键字创建一个Java 对象的时候,它会超出作用域的范 

围之外。所以假若使用下面这段代码:  

  

{  

String s = new String(〃a string〃);  

} /* 作用域的终点 */  

  

那么句柄 s 会在作用域的终点处消失。然而,s指向的 String 对象依然占据着内存空间。在上面这段代码 

里,我们没有办法访问对象,因为指向它的唯一一个句柄已超出了作用域的边界。在后面的章节里,大家还 

会继续学习如何在程序运行期间传递和复制对象句柄。  

这样造成的结果便是:对于用 new 创建的对象,只要我们愿意,它们就会一直保留下去。这个编程问题在C 

和C++里特别突出。看来在C++里遇到的麻烦最大:由于不能从语言获得任何帮助,所以在需要对象的时候, 

根本无法确定它们是否可用。而且更麻烦的是,在 C++里,一旦工作完成,必须保证将对象清除。  

这样便带来了一个有趣的问题。假如Java 让对象依然故我,怎样才能防止它们大量充斥内存,并最终造成程 

序的“凝固”呢。在C++里,这个问题最令程序员头痛。但 Java 以后,情况却发生了改观。Java 有一个特别 

的“垃圾收集器”,它会查找用new 创建的所有对象,并辨别其中哪些不再被引用。随后,它会自动释放由 

那些闲置对象占据的内存,以便能由新对象使用。这意味着我们根本不必操心内存的回收问题。只需简单地 

创建对象,一旦不再需要它们,它们就会自动离去。这样做可防止在 C++里很常见的一个编程问题:由于程 

序员忘记释放内存造成的“内存溢出”。  



2。4 新建数据类型:类  



如果说一切东西都是对象,那么用什么决定一个“类”(Class)的外观与行为呢?换句话说,是什么建立起 

了一个对象的“类型”(Type )呢?大家可能猜想有一个名为“type”的关键字。但从历史看来,大多数面 

向对象的语言都用关键字“class”表达这样一个意思:“我准备告诉你对象一种新类型的外观”。class 关 

键字太常用了,以至于本书许多地方并没有用粗体字或双引号加以强调。在这个关键字的后面,应该跟随新 

数据类型的名称。例如:  

class ATypeName {/*类主体置于这里}  

这样就引入了一种新类型,接下来便可用new 创建这种类型的一个新对象:  

ATypeName a = new ATypeName();  

在ATypeName 里,类主体只由一条注释构成(星号和斜杠以及其中的内容,本章后面还会详细讲述),所以 

并不能对它做太多的事情。事实上,除非为其定义了某些方法,否则根本不能指示它做任何事情。  



2。4。1  字段和方法  



定义一个类时(我们在 Java 里的全部工作就是定义类、制作那些类的对象以及将消息发给那些对象),可在 

自己的类里设置两种类型的元素:数据成员(有时也叫“字段”)以及成员函数(通常叫“方法”)。其 

中,数据成员是一种对象(通过它的句柄与其通信),可以为任何类型。它也可以是主类型(并不是句柄) 

之一。如果是指向对象的一个句柄,则必须初始化那个句柄,用一种名为“构建器”(第4 章会对此详述) 

的特殊函数将其与一个实际对象连接起来(就象早先看到的那样,使用new 关键字)。但若是一种主类型, 

则可在类定义位置直接初始化(正如后面会看到的那样,句柄亦可在定义位置初始化)。  



                                                                          49 


…………………………………………………………Page 51……………………………………………………………

每个对象都为自己的数据成员保有存储空间;数据成员不会在对象之间共享。下面是定义了一些数据成员的 

类示例:  

  

class DataOnly {  

  int i;  

  float f;  

  boolean b;  

}  

  

这个类并没有做任何实质性的事情,但我们可创建一个对象:  

DataOnly d = new DataOnly();  

可将值赋给数据成员,但首先必须知道如何引用一个对象的成员。为达到引用对象成员的目的,首先要写上 

对象句柄的名字,再跟随一个点号(句点),再跟随对象内部成员的名字。即“对象句柄。成员”。例如:  

d。i = 47;  

d。f = 1。1f;  

d。b = false;  

一个对象也可能包含了另一个对象,而另一个对象里则包含了我们想修改的数据。对于这个问题,只需保持 

 “连接句点”即可。例如:  

myPlane。leftTank。capacity = 100;  

除容纳数据之外,DataOnly 类再也不能做更多的事情,因为它没有成员函数(方法)。为正确理解工作原 

理,首先必须知道“自变量”和“返回值”的概念。我们马上就会详加解释。  

  

1。 主成员的默认值  

若某个主数据类型属于一个类成员,那么即使不明确(显式)进行初始化,也可以保证它们获得一个默认 

值。  

  

主类型 默认值  

  

Boolean false  

Char 'u0000'(null)  

byte (byte)0  

short (short)0  

int 0  

long 0L  

float 0。0f  

double 0。0d  

  

一旦将变量作为类成员使用,就要特别注意由 Java 分配的默认值。这样做可保证主类型的成员变量肯定得到 

了初始化(C++不具备这一功能),可有效遏止多种相关的编程错误。  

然而,这种保证却并不适用于“局部”变量——那些变量并非一个类的字段。所以,假若在一个函数定义中 

写入下述代码:  

int x;  

那么x 会得到一些随机值(这与C 和C++是一样的),不会自动初始化成零。我们责任是在正式使用x 前分 

配一个适当的值。如果忘记,就会得到一条编译期错误,告诉我们变量�
返回目录 上一页 下一页 回到顶部 赞(11) 踩(15)
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!