友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
Java编程思想第4版[中文版](PDF格式)-第17部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
配一个适当的值。如果忘记,就会得到一条编译期错误,告诉我们变量可能尚未初始化。这种处理正是 Java
优于C++的表现之一。许多 C++编译器会对变量未初始化发出警告,但在 Java 里却是错误。
2。5 方法、自变量和返回值
迄今为止,我们一直用“函数”(Function )这个词指代一个已命名的子例程。但在Java 里,更常用的一个
词却是“方法”(Method),代表“完成某事的途径”。尽管它们表达的实际是同一个意思,但从现在开
始,本书将一直使用“方法”,而不是“函数”。
Java 的“方法”决定了一个对象能够接收的消息。通过本节的学习,大家会知道方法的定义有多么简单!
方法的基本组成部分包括名字、自变量、返回类型以及主体。下面便是它最基本的形式:
50
…………………………………………………………Page 52……………………………………………………………
返回类型 方法名 ( /* 自变量列表*/ ) {/* 方法主体 */}
返回类型是指调用方法之后返回的数值类型。显然,方法名的作用是对具体的方法进行标识和引用。自变量
列表列出了想传递给方法的信息类型和名称。
Java 的方法只能作为类的一部分创建。只能针对某个对象调用一个方法(注释③),而且那个对象必须能够
执行那个方法调用。若试图为一个对象调用错误的方法,就会在编译期得到一条出错消息。为一个对象调用
方法时,需要先列出对象的名字,在后面跟上一个句点,再跟上方法名以及它的参数列表。亦即“对象名。方
法名(自变量1,自变量2,自变量3。。。)。举个例子来说,假设我们有一个方法名叫f(),它没有自变量,返
回的是类型为int 的一个值。那么,假设有一个名为 a 的对象,可为其调用方法f(),则代码如下:
int x = a。f();
返回值的类型必须兼容 x 的类型。
象这样调用一个方法的行动通常叫作“向对象发送一条消息”。在上面的例子中,消息是f(),而对象是 a。
面向对象的程序设计通常简单地归纳为“向对象发送消息”。
③:正如马上就要学到的那样,“静态”方法可针对类调用,毋需一个对象。
2。5。1 自变量列表
自变量列表规定了我们传送给方法的是什么信息。正如大家或许已猜到的那样,这些信息——如同Java 内其
他任何东西——采用的都是对象的形式。因此,我们必须在自变量列表里指定要传递的对象类型,以及每个
对象的名字。正如在Java 其他地方处理对象时一样,我们实际传递的是“句柄”(注释④)。然而,句柄的
类型必须正确。倘若希望自变量是一个“字串”,那么传递的必须是一个字串。
④:对于前面提及的“特殊”数据类型 boolean,char,byte,short,int,long,,float 以及double 来
说是一个例外。但在传递对象时,通常都是指传递指向对象的句柄。
下面让我们考虑将一个字串作为自变量使用的方法。下面列出的是定义代码,必须将它置于一个类定义里,
否则无法编译:
int storage(String s) {
return s。length() * 2;
}
这个方法告诉我们需要多少字节才能容纳一个特定字串里的信息(字串里的每个字符都是 16位,或者说 2 个
字节、长整数,以便提供对Unicode 字符的支持)。自变量的类型为String,而且叫作 s。一旦将s 传递给
方法,就可将它当作其他对象一样处理(可向其发送消息)。在这里,我们调用的是 length()方法,它是
String 的方法之一。该方法返回的是一个字串里的字符数。
通过上面的例子,也可以了解 return 关键字的运用。它主要做两件事情。首先,它意味着“离开方法,我已
完工了”。其次,假设方法生成了一个值,则那个值紧接在return 语句的后面。在这种情况下,返回值是通
过计算表达式“s。length()*2”而产生的。
可按自己的愿望返回任意类型,但倘若不想返回任何东西,就可指示方法返回void (空)。下面列出一些例
子。
boolean flag() { return true; }
float naturalLogBase() { return 2。718; }
void nothing() { return; }
void nothing2() {}
若返回类型为void,则return 关键字唯一的作用就是退出方法。所以一旦抵达方法末尾,该关键字便不需
要了。可在任何地方从一个方法返回。但假设已指定了一种非 void 的返回类型,那么无论从何地返回,编译
器都会确保我们返回的是正确的类型。
到此为止,大家或许已得到了这样的一个印象:一个程序只是一系列对象的集合,它们的方法将其他对象作
51
…………………………………………………………Page 53……………………………………………………………
为自己的自变量使用,而且将消息发给那些对象。这种说法大体正确,但通过以后的学习,大家还会知道如
何在一个方法里作出决策,做一些更细致的基层工作。至于这一章,只需理解消息传送就足够了。
2。6 构建 Java 程序
正式构建自己的第一个 Java 程序前,还有几个问题需要注意。
2。6。1 名字的可见性
在所有程序设计语言里,一个不可避免的问题是对名字或名称的控制。假设您在程序的某个模块里使用了一
个名字,而另一名程序员在另一个模块里使用了相同的名字。此时,如何区分两个名字,并防止两个名字互
相冲突呢?这个问题在 C 语言里特别突出。因为程序未提供很好的名字管理方法。C++的类(即Java 类的基
础)嵌套使用类里的函数,使其不至于同其他类里的嵌套函数名冲突。然而,C++仍然允许使用全局数据以及
全局函数,所以仍然难以避免冲突。为解决这个问题,C++用额外的关键字引入了“命名空间”的概念。
由于采用全新的机制,所以Java 能完全避免这些问题。为了给一个库生成明确的名字,采用了与Internet
域名类似的名字。事实上,Java 的设计者鼓励程序员反转使用自己的Internet 域名,因为它们肯定是独一
无二的。由于我的域名是BruceEckel。,所以我的实用工具库就可命名为
。bruceeckel。utility。foibles。反转了域名后,可将点号想象成子目录。
在Java 1。0 和 Java 1。1 中,域扩展名,edu,org,net 等都约定为大写形式。所以库的样子就变成:
。bruceeckel。utility。foibles。然而,在Java 1。2 的开发过程中,设计者发现这样做会造成一些问题。
所以目前的整个软件包都以小写字母为标准。
Java 的这种特殊机制意味着所有文件都自动存在于自己的命名空间里。而且一个文件里的每个类都自动获得
一个独一无二的标识符(当然,一个文件里的类名必须是唯一的)。所以不必学习特殊的语言知识来解决这
个问题——语言本身已帮我们照顾到这一点。
2。6。2 使用其他组件
一旦要在自己的程序里使用一个预先定义好的类,编译器就必须知道如何找到它。当然,这个类可能就在发
出调用的那个相同的源码文件里。如果是那种情况,只需简单地使用这个类即可——即使它直到文件的后面
仍未得到定义。Java 消除了“向前引用”的问题,所以不要关心这些事情。
但假若那个类位于其他文件里呢?您或许认为编译器应该足够“联盟”,可以自行发现它。但实情并非如
此。假设我们想使用一个具有特定名称的类,但那个类的定义位于多个文件里。或者更糟,假设我们准备写
一个程序,但在创建它的时候,却向自己的库加入了一个新类,它与现有某个类的名字发生了冲突。
为解决这个问题,必须消除所有潜在的、纠缠不清的情况。为达到这个目的,要用 import 关键字准确告诉
Java 编译器我们希望的类是什么。import 的作用是指示编译器导入一个“包”——或者说一个“类库”(在
其他语言里,可将“库”想象成一系列函数、数据以及类的集合。但请记住,Java 的所有代码都必须写入一
个类中)。
大多数时候,我们直接采用来自标准Java 库的组件(部件)即可,它们是与编译器配套提供的。使用这些组
件时,没有必要关心冗长的保留域名;举个例子来说,只需象下面这样写一行代码即可:
import java。util。Vector;
它的作用是告诉编译器我们想使用 Java 的Vector 类。然而,util 包含了数量众多的类,我们有时希望使用
其中的几个,同时不想全部明确地声明它们。为达到这个目的,可使用“*”通配符。如下所示:
import java。util。*;
需导入一系列类时,采用的通常是这个办法。应尽量避免一个一个地导入类。
2。6。3 static 关键字
通常,我们创建类时会指出那个类的对象的外观与行为。除非用new 创建那个类的一个对象,否则实际上并
未得到任何东西。只有执行了 new 后,才会正式生成数据存储空间,并可使用相应的方法。
但在两种特殊的情形下,上述方法并不堪用。一种情形是只想用一个存储区域来保存一个特定的数据——无
论要创建多少个对象,甚至根本不创建对象。另一种情形是我们需要一个特殊的方法,它没有与这个类的任
何对象关联。也就是说,即使没有创建对象,也需要一个能调用的方法。为满足这两方面的要求,可使用
static (静态)关键字。一旦将什么东西设为static,数据或方法就不会同那个类的任何对象实例联系到一
起。所以尽管从未创建那个类的一个对象,仍能调用一个 static方法,或访问一些 static数据。而在这之
52
…………………………………………………………Page 54……………………………………………………………
前,对于非 static数据和方法,我们必须创建一个对象,并用那个对象访问数据或方法。这是由于非
static数据和方法必须知道它们操作的具体对象。当然,在正式使用前,由于static方法不需要创建任何
对象,所以它们不可简单地调用其他那些成员,同时不引用一个已命名的对象,从而直接访问非 static成员
或方法(因为非static成员和方法必须同一个特定的对象关联到一起)。
有些面向对象的语言使用了“类数据”和“类方法”这两个术语。它们意味着数据和方法只是为作为一个整
体的类而存在的,并不是为那个类的任何特定对象。有时,您会在其他一些Java 书刊里发现这样的称呼。
为了将数据成员或方法设为static,只需在定义前置和这个关键字即可。例如,下述代码能生成一个 static
数据成员,并对其初始化:
class StaticTest {
Static int i = 47;
}
现在,尽管我们制作了两个StaticTest 对象,但它们仍然只占据StaticTest。i 的一个存储空间。这两个对
象都共享同样的i。请考察下述代码:
StaticTest st1 = new StaticTest();
StaticTest st2 = new StaticTest();
此时,无论 st1。i还是 st2。i都有同样的值 47,因为它们引用的是同样的内存区域。
有两个办法可引用一个 static变量。正如上面展示的那样,可通过一个对象命名它,如 st2。i。亦可直接用
它的类名引用,而这在非静态成员里是行不通的(最好用这个办法引用 static 变量,因为它强调了那个变量
的“静态”本质)。
StaticTest。i++;
其中,++运算符会使变量增值。此时,无论 st1。i 还是 st2。i 的值都是48。
类似的逻辑也适用于静态方法。既可象对其他任何方法那样通过一个对象引用静态方法,亦可用特殊的语法
格式“类名。方法()”加以引用。静态方法的定义是类似的:
class StaticFun {
static void incr() { StaticTest。i++; }
}
从中可看出,StaticFun 的方法 incr()使静态数据 i增值。通过对象,可用典型的方法调用 incr():
StaticFun sf = new StaticFun();
sf。incr();
或者,由于 incr()是一种静态方法,所以可通过它的类直接调用:
StaticFun。incr();
尽管是“静态”的,但只要应用于一个数据成员,就会明确改变数据的创建方式(一个类一个成员,以及每
个对象一个非静态成员)。若应用于一个方法,就没有那么戏剧化了。对方法来说,static一项重要的用途
就是帮助我们在不必创建对象的前提下调用那个方法。正如以后会看到的那样,这一点是至关重要的——特
别是在定义程序运行入口方法main()的时候。
和其他任何方法一样,static方法也能创建自己类型的命名对象。所以经常把 static方法作为一个“领头
羊”使用,用它生成一系列自己类型的“实例”。
2。7 我们的第一个 Java 程序
最后,让我们正式编一个程序(注释⑤)。它能打印出与当前运行的系统有关的资料,并利用了来自Java 标
准库的 System 对象的多种方法。注意这里引入了一种额外的注释样式:“//”。它表示到本行结束前的所有
内容都是注释:
// Property。java
import java。util。*;
public class Property {
public static void main(String'' args) {
System。out。println(new Date());
Properties p = System。getProperties();
53
…………………………………………………………Page 55……………………………………………………………
p。list(System。out);
System。out。println(〃……Memory Usage:〃);
Runtime rt = Runtime。getRuntime();
System。out。println(〃Total Memory = 〃
+ rt。totalMemory()
+ 〃 Free Memory = 〃
+ rt。freeMemory());
}
}
⑤:在某些编程环境里,程序会在屏幕上一切而过,甚至没机会看到结果。可将下面这段代码置于 main()的
末尾,用它暂停输出:
try {
Thread。currentThread()。sleep(5 * 1000);
} catch(InterruptedException e) {}
}
它的作用是暂停输出5 秒钟。这段代码涉及的一些概念要到本书后面才会讲到。所以目前不必深究,只知道
它是让程序暂停的一个技巧便可。
在每个程序文件的开头,都必须放置一个 import 语句,导入那个文件的代码里要用到的所有额外的类。注意
我们说它们是“额外”的,因为一个特殊的类库会自动导入每个Java 文件:java。lang。启动您的Web 浏览
器,查看由 Sun 提供的用户文档(如果尚未从 http://java。sun。 下载,或用其他方式安装了Java 文
档,请立即下载)。在 packages。html 文件里,可找到Java 配套提供的所有类库名称。请选择其中的
java。lang。在“Class Index”下面,可找到属于那个库的全部类的列表。由于 java。lang 默认进入每个
Java 代码文件,所以这些类在任何时候都可直接使用。在这个列表里,可发现System 和 Runtime,我们在
Property。java 里用到了它们。java。lang 里没有列出 Date 类,所以必须导入另一个类库才能使用它。如果
不清楚一个特定的类在哪个类库里,或者想检视所有的类,可在Java 用户文档里选择“Class Hierarchy”
(类分级结构)。在Web 浏览器中,虽然要花不短的时间来建立这个结构,但可清楚找到与Java 配套提供的
每一个类。随后,可用浏览器的“查找”(Find )功能搜索关键字“Date ”。经这样处理后,可发现我们的
搜索目标以 java。util。Date 的形式列出。我们终于知道它位于util 库里,所以必须导入 java。util。*;否
则便不能使用Date 。
观察packages。html 文档最开头的部分(我已将其设为自己的默认起始页),请选择java。lang,再选
System。这时可看到System 类有几个字段。若选择 out,就可知道它是一个static PrintStream 对象。由
于它是“静态”的,所以不需要我们创建任何东西。out 对象肯定是 3,所以只需直接用它即可。我们能对这
个out 对象做的事情由它的类型决定:PrintStream。PrintStream 在说明文字中以一个超链接的形式列出,
这一点做得非常方便。所以假若单击那个链接,就可看到能够为PrintStream 调用的所有方法。方法的数量
不少,本书后面会详细介绍。就目前来说,我们感兴趣的只有 println()。它的意思是“把我给你的东西打
印到控制台,并用一个新行结束”。所以在任何Java 程序中,一旦要把某些内容打印到控制台,就可条件反
射地写上System。out。println(〃内容〃)。
类名与文件是一样的。若象现在这样创建一个独立的程序,文件中的一个类必须与文件同名(如果没这样
做,编译器会及时作出反应)。类里必须包含一个名为main()的方法,形式如下:
public static void main(String'' args) {
其中,关键字“public”意味着方法可由外部世界调用(第5 章会详细解释)。main()的自变量是包含了
String 对象的一个数组。args 不会在本程序中用到,但需要在这个地方列出,因为它们保存了在命令行调用
的自变量。
程序的第一行非常有趣:
System。out。println(new Date());
请观察它的自变量:创建Date 对象唯一的目的就是将它的值发送给 println()。一旦这个语句执行完毕,
Date 就不再需要。随之而来的“垃圾收集器”会发现这一情况,并在任何可能的时候将其回收。事实上,我
们没太大的必要关心“清除”的细节。
第二行调用了System。getProperties() 。若用Web 浏览器查看联机用户文档,就可知道 getProperties()是
54
…………………………………………………………Page 56……………………………………………………………
System 类的一个 static方法。由于它是“静态”的,所以不必创建任何对象便可调用该方法。无论是否存
在该类的一个对象,static 方法随时都可使用。调用getProperties()时,它会将系统属性作为 Properties
类的一个对象生成(注意Properties 是“属性”的意思)。随后的的句柄保存在一个名为p 的Properties
句柄里。在第三行,大家可看到Properties 对象有一个名为 list()的方法,它将自己的全部内容都发给一
个我们作为自变量传递的PrintStream 对象。
main()的第四和第六行是典型的打印语句。注意为了打印多个String 值,用加号(+)分隔它们即可。然
而,也要在这里注意一些奇怪的事情。在 String 对象中使用时,加号并不代表真正的“相加”。处理字串
时,我们通常不必考虑“+”的任何特殊含义。但是,Java 的String 类要受一种名为“运算符过载”的机制
的制约。也就是说,只有在随同String 对象使用时,加号才会产生与其他任何地方不同的表现。对于字串,
它的意思是“连接这两个字串”。
但事情到此并未结束。请观察下述语句:
System。out。println(〃Total Memory = 〃
+ rt。totalMemory()
+ 〃 Free Memory = 〃
+ rt。freeMemory());
其中,totalMemory()和freeMemory ()返回的是数值,并非String 对象。如果将一个数值“
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!