第1章 Java的起源
对于计算机语言的发展史,业界一般认为:B语言导致了C语言的诞生,C语言演变出
了C++语言,而C++语言将让位于Java语言.要想更好地了解Java语言,就必须了解它产生
的原因,推动它发展的动力,以及它对其他语言的继承.像以前其他成功的计算机语言一
样,Java继承了其他语言的先进原理,同时又因其独特的环境要求而提出了一些创新性的
概念.在这本书的其他各章中,将从实用的角度,对Java语言,库及应用程序进行包括语
法在内的详细介绍.在本章里,我们将介绍Java语言产生的背景,发展过程,以及使它变
得如此重要的原因.
尽管Java语言已和Internet的在线环境密不可分,但首先应该注意到的最重要一点是:
它是一种程序语言.计算机语言的革新和发展需要2个基本因素的驱动:
适应正在变化的环境和需求
实现编程艺术的完善与提高
下面你将看到,Java也正是在这两个因素的驱动下产生的.
1.1 Java的由来
Java总是和C++联系在一起,而C++则是从C语言派生而来的,所以Java语言继承了这
两种语言的大部分特性.Java的语法是从C继承的,Java许多面向对象的特性受到C++的影
响.事实上,Java中几个自定义的特性都来自于或可以追溯到它的前驱.而且,Java语言的
产生与过去30年中计算机语言细致改进和不断发展密切相关.基于这些原因,本节将按顺
序回顾促使Java产生的事件和推动力.正如你将看到的一样,每一次语言设计的革新都是
因为先前的语言不能解决目前遇到的基本问题而引起.Java也不例外.
1.1.1 现代的编程语言的诞生:C语言
C语言的产生震撼了整个计算机界.它的影响不应该被低估,因为它从根本上改变了
编程的方法和思路.C语言的产生是人们追求结构化,高效率,高级语言的直接结果,可
用它替代汇编语言开发系统程序.当设计一种计算机语言时,经常要从以下几方面进行权
衡:
第1部分 Java语言
Title:Java2参考大全(第四版)
Description:Java2:The Complete Reference
Copyright:Copyright (c) 2001 by The McGraw-Hill Companies.
Author:Herbert Schildt
Version:Fourth Edition
Editor:Windflowers
2 第1部分 Java语言
易用性与功能
安全性和效率性
稳定性和可扩展性
C语言出现以前,程序员们不得不经常在有优点但在某些方面又有欠缺的语言之间做
出选择.例如,尽管公认FORTRAN在科学计算应用方面可以编写出相当高效的程序,但
它不适于编写系统程序.BASIC虽然容易学习,但功能不够强大,并且谈不上结构化,这
使它应用到大程序的有效性受到怀疑.汇编语言虽能写出高效率的程序,但是学习或有效
地使用它却是不容易的.而且,调试汇编程序也相当困难.
另一个复杂的问题是,早期设计的计算机语言(如BASIC,COBOL,FORTRAN等)
没有考虑结构化设计原则,使用GOTO语句作为对程序进行控制的一种主要方法.这样做
的结果是,用这些语言编写的程序往往成了"意大利面条式的程序代码",一大堆混乱的
跳转语句和条件分支语句使得程序几乎不可能被读懂.Pascal虽然是结构化语言,但它的设
计效率比较低,而且缺少几个必需的特性,因而无法在大的编程范围内使用(特别是,给
定的Pascal的标准语言在特定时间是可用的,但将Pascal作为系统级编码是不切实际的).
因此,在C语言产生以前,没有任何一种语言能完全满足人们的需要,但人们对这样
一种语言的需要却是迫切的.在20世纪70年代初期,计算机革命开始了,对软件的需求量
日益增加,使用早期的计算机语言进行软件开发根本无法满足这种需要.学术界付出很多
努力,尝试创造一种更好的计算机语言.但是,促使C语言诞生的另一个,也许是最重要
的因素,是计算机硬件资源的富余带来了机遇.计算机不再像以前那样被紧锁在门里,程
序员们可以随意使用计算机,可以随意进行自由尝试,因而也就有了可以开发适合自己使
用的工具的机会.所以,在C语言诞生的前夕,计算机语言向前飞跃的时机已经成熟.
在Dennis Ritchie第一个发明和实现在DEC PDP-11上运行UNIX操作系统时,一种更古
老的由Martin Richards设计的BCPL语言导致了C语言的产生.受BCPL语言的影响,由Ken
Thompson发明的B语言,在20世纪70年代逐渐向C语言发展演变.在此后的许多年里,由
Brian Kernighan和Dennis Ritchie编写的《The C Programming Language》(Prentice-Hall,1978)
被认为是事实上的C语言标准,该书认为C只是支持UNIX 操作系统的一种语言.1989年12
月,美国国家标准化组织( ANSI )制定了C语言的标准,C语言被正式标准化.
许多人认为C语言的产生标志着现代计算机语言时代的开始.它成功地综合处理了长
期困扰早期语言的矛盾属性.C语言是功能强大,高效的结构化语言,简单易学,而且它
还包括一个无形的方面:它是程序员自己的语言.在C语言出现以前,计算机语言要么被
作为学术实验而设计,要么由官僚委员会设计.而C语言不同.它的设计,实现,开发由
真正的从事编程工作的程序员来完成,反映了现实编程工作的方法.它的特性经由实际运
用该语言的人们不断去提炼,测试,思考,再思考,使得C语言成为程序员们喜欢使用的
语言.确实,C语言迅速吸引了许多狂热的追随者,因而很快受到许多程序员的青睐.简
言之,C语言是由程序员设计并由他们使用的一种语言.正如你将看到的,Java继承了这个
思想.
第1章 Java的起源 3
1.1.2 对C++的需要
在20世纪70年代末和80年代初,C成为了主流的计算机编程语言,至今仍被广泛使用.
你也许会问,既然C是一种成功且有用的语言,为什么还需要新的计算机语言 答案是复
杂性(complexity).程序越来越复杂这一事实贯穿编程语言的历史.C++正是适应了这一
需求.下面介绍为什么对程序复杂性的更好管理是C++产生的基本条件.
自从计算机发明以来,编程方法经历了戏剧性的变化.例如,当计算机刚发明出来时,
编程是通过面板触发器用人工打孔的办法输入二进制机器指令来实现的.对于只有几百行
的程序,这种办法是可行的.随着程序不断增大,人们发明了汇编语言,它通过使用符号
来代替机器指令,这样程序员就能处理更大,更复杂的程序.随着程序的进一步增大,高
级语言产生了,它给程序员提供了更多的工具来处理复杂性问题.
第一个被广泛使用的高级语言当然是FORTRAN.尽管FORTRAN最初给人留下了深刻
的印象,但它无法开发出条理清楚易于理解的程序.20世纪60年代提出了结构化编程方法.
这种结构化的编程思想被像C这样的语言所应用,第一次使程序员可以相对轻松地编写适
度复杂的程序.然而,当一个工程项目达到一定规模后,即使使用结构化编程方法,编程
人员也无法对它的复杂性进行有效管理.20世纪80年代初期,许多工程项目的复杂性都超
过了结构化方法的极限.为解决这个问题,面向对象编程(object-oriented programming,
OOP)新方法诞生了.面向对象的编程在这本书的后面详细讨论,但在这里给出一个简短
的定义:面向对象的编程是通过使用继承性,封装性和多态性来帮助组织复杂程序的编程
方法.
总之,尽管C是世界上伟大的编程语言之一,但它处理复杂性的能力有限.一旦一个
程序的代码超过25 000~100 000行,就很难从总体上把握它的复杂性了.C++突破了这个限
制,帮助程序员理解并且管理更大的程序.
1979年,当Bjarne Stroustrup在新泽西州的Murray Hill实验室工作时,发明了C++.
Stroustrup 最初把这种新语言称为"带类的C".1983年,改名为C++.C++通过增加面向
对象的特性扩充了C.因为C++产生在C的基础之上,因此它包括了C所有的特征,属性和
优点.这是C++作为语言成功的一个关键原因.C++的发明不是企图创造一种全新的编程语
言,而是对一个已经高度成功的语言的改进.C++在1997年11月被标准化,目前的标准是
ANSI/ISO.
1.1.3 Java出现的时机已经到来
在20世纪80年代末和90年代初,使用面向对象编程的C++语言占主导地位.的确,有
一段时间程序员似乎都认为已经找到了一种完美的语言.因为C++有面向对象的特征,又
有C语言高效和格式上的优点,因此它是一种可以被广泛应用的编程语言.然而,就像过
去一样,推动计算机语言进化的力量正在酝酿.在随后的几年里,万维网(WWW)和Internet
达到临界状态.这个事件促成编程的另一场革命.
4 第1部分 Java语言
1.2 Java的产生
Java是由James Gosling,Patrick Naughton,Chris Warth,Ed Frank和Mike Sheridan于1991
年在Sun Microsystems公司设计出来的.开发第一个版本花了18个月.该语言开始名叫
"Oak",于1995年更名为"Java".从1992 的秋天Oak问世到1995的春天公开发布Java
语言,许多人对Java的设计和改进做出了贡献.Bill Joy,Arthur van Hoff,Jonathan Payne,
Frank Yellin和Tim Lindholm是主要的贡献者,正是他们的贡献使最初原型得以成熟.
说起来多少有些令人吃惊,Java的最初推动力并不是因特网!而是源于对独立于平台
(也就是体系结构中立)语言的需要,这种语言可创建能够嵌入微波炉,遥控器等各种家
用电器设备的软件.用作控制器的CPU芯片是多种多样的,但C和C++以及其他绝大多数语
言的缺点是只能对特定目标进行编译.尽管为任何类型的CPU芯片编译C++程序是可能的,
但这样做需要一个完整的以该CPU为目标的C++编译器,而创建编译器是一项既耗资巨大
又耗时较长的工作.因此需要一种简单且经济的解决方案.为了找到这样一种方案,Gosling
和其他人开始一起致力于开发一种可移植,跨平台的语言,该语言能够生成运行于不同环
境,不同CPU芯片上的代码.他们的努力最终促成了Java的诞生.
在Java的一些细节被设计出来的同时,第二个并且也是最重要的因素出现了,该因素
将对Java的未来起着至关重要的作用.这第二个因素当然就是万维网(WWW).如果万维
网(WWW)的成型和Java的实现不是同时发生的话,那么Java可能保持它有用,但默默无
闻的用于电子消费品编程语言的状态.然而,随着万维网的出现,Java被推到计算机语言
设计的最前沿,因为万维网也需要可移植的程序.
绝大多数程序员在涉足编程领域时就知道可移植的程序像他们的理想一样难以捉摸.
尽管人们对高效的,可移植的(独立于平台)编程方式的追寻几乎和编程历史一样久远,
但它总是让位于其他的更为紧迫的问题.此外,因为计算机业被Intel,Macintosh和UNIX
这3个竞争对手垄断,大多数程序员都在其中的某个领域内长期工作,所以对可移植语言的
需求就不是那么迫切.但是,随着因特网和Web的出现,关于可移植性语言的旧问题又被
提了出来.毕竟,因特网由不同的,分布式的系统组成,其中包括各种类型的计算机,操
作系统和CPU.尽管许多类型的平台都可以与因特网连接,但用户仍希望他们能够运行同
样的程序.曾经是一个令人烦恼却无需优先考虑的问题现在变成了急需解决的问题.
1993年,Java设计小组的成员发现他们在编制嵌入式控制器代码时经常遇到的可移植
性问题,在编制因特网代码的过程中也出现了.事实上,开始被设计为解决小范围问题的
Java语言同样可以被用在大范围的因特网上.这个认识使他们将Java的重心由电子消费品转
移到Internet编程.因此,中立体系结构编程语言的需要是促使Java诞生的源动力,而Internet
却最终导致了Java的成功.
正如前面提到的,Java的大部分特性是从C和C++中继承的.Java设计人员之所以故意
这么做,主要是因为他们觉得,在新语言中使用熟悉的C语法及模仿C++面向对象的特性,
将使他们的语言对经验丰富的C/C++程序员有更大的吸引力.除了表面类似外,其他一些
促使C和C++成功的因素也帮了Java的忙.首先,Java的设计,测试,精炼由真正从事编程
第1章 Java的起源 5
工作的人员完成,它根植于设计它的人员的需要和经验,因而也是一个程序员自己的语言.
其次,Java是紧密结合的且逻辑上是协调一致的.最后,除了那些Internet环境强加的约束
以外,Java给了编程人员完全的控制权.如果你程序编的好,你编写的程序就能反映出这
一点.相反,如果你的编程手法拙劣,也能在你的程序中反映出来.换一种说法,Java并
不是训练新手的语言,而是供专业编程人员使用的语言.
由于Java和C++之间的相似性,容易使人将Java简单地想象为"C++的版本".但其实
这是一种误解.Java在实践和理论上都与C++有重要的不同点.尽管Java受到C++的影响,
但它并不是C++的增强版.例如,Java与C++既不向上兼容,也不向下兼容.当然,Java与
C++的相似之处也是很多的,如果你是一个C++程序员,你会感觉到对Java非常熟悉.另外
一点是:Java并不是用来取代C++的,设计Java是为了解决某些特定的问题,而设计C++是
为了解决另外一类完全不同的问题.两者将长时间共存.
正如本章开始提到的,计算机语言的革新靠两个因素驱动:对计算环境改变的适应和
编程艺术的进步.环境的变化促使Java这种独立于平台的语言注定成为Internet上的分布式
编程语言.同时,Java也改变了人们的编程方式,特别是Java对C++使用的面向对象范例进
行的增强和完善.所以,Java不是孤立存在的一种语言,而是计算机语言多年来的演变结
果.仅这个事实就足以证明Java在计算机语言历史上的地位.Java对Internet编程的影响就
如同C对系统编程的影响一样:革命的力量将改变世界.
1.3 Java对Internet为什么重要
Internet使Java成为网上最流行的编程语言,同时Java对Internet的影响也意义深远.原
因相当简单:Java扩展了可以在赛百空间自由流动的对象的世界.在网络中,有两大类对
象在服务器和个人计算机之间传输:被动的信息和动态的,主动的程序.例如,当你阅读
电子邮件时,你在看被动的数据.甚至当你下载一个程序时,该程序的代码也是被动的数
据,直到你执行它为止.但是,可以传输到个人计算机的另一类对象却是:动态的,可自
运行的程序,虽然这类程序是客户机上的活动代理,但却是由服务器来初始化的.例如,
被服务器用来正确地显示服务器传送数据的程序.
网上程序在动态性上是令人满意的,但它们在安全性和可移植性方面也显示出严重的
缺陷.在Java产生以前,当前赛百空间有一半的对象实体无法进入网络世界,是Java为它们
打开了便利之门,而且在这个过程中定义了一种全新的程序形式:applet(小应用程序).
1.3.1 Java小应用程序和应用程序
Java可用来生成两类程序:应用程序(applications)和Java applet(小应用程序).应
用程序是可以在你的计算机的操作系统中运行的程序,从这一方面来说,用Java编制的应
用程序多多少少与使用C或C++编制的应用程序有些类似.在创建应用程序时,Java与其他
计算机语言没有大的区别.而Java的重要性就在于它具有编制小应用程序的功能.小应用
程序是可以在Internet中传输并在兼容Java的Web浏览器中运行的应用程序.小应用程序实
际上就是小型的Java程序,能像图像文件,声音文件和视频片段那样通过网络动态下载,
6 第1部分 Java语言
它与其他文件的重要差别是,小应用程序是一个智能的程序,能对用户的输入作出反应,
并且能动态变化,而不是一遍又一遍地播放同一动画或声音.
如果Java不能解决两个关于小应用程序的最棘手的问题:安全性和可移植性,那么小
应用程序就不会如此令人激动.在继续下一个话题之前,让我们先说明以下这两个术语对
Internet的意义.
1.3.2 安全性
正如你知道的那样,每次当你下载一个"正常"的程序时,你都要冒着被病毒感染的
危险.在Java出现以前,大多数用户并不经常下载可执行的程序文件;即使下载了程序,
在运行它们以前也都要进行病毒检查.尽管如此,大多数用户还是担心他们的系统可能被
病毒感染.除了病毒,另一种恶意的程序也必须警惕.这种恶意的程序可通过搜索你计算
机本地文件系统的内容来收集你的私人信息,例如信用卡号码,银行账户结算和口令.Java
在网络应用程序和你的计算机之间提供了一道防火墙(firewall),消除了用户的这些顾虑.
当使用一个兼容Java的Web浏览器时,你可以安全地下载Java小应用程序,不必担心病
毒的感染或恶意的企图.Java实现这种保护功能的方式是,将Java程序限制在Java运行环境
中,不允许它访问计算机的其他部分,后面将介绍这个过程是如何实现的.下载小应用程
序并能确保它对客户机的安全性不会造成危害是Java的一个最重要的方面.
1.3.3 可移植性
正如前面所讨论的,许多类型的计算机和操作系统都连接到Internet上.要使连接到
Internet上的各种各样的平台都能动态下载同一个程序,就需要有能够生成可移植性执行代
码的方法.很快你将会看到,有助于保证安全性的机制同样也有助于建立可移植性.实际
上,Java对这两个问题的解决方案是优美的也是高效的.
1.4 Java的魔力:字节码
Java解决上述两个问题——安全性和可移植性的关键在于Java编译器的输出并不是可
执行的代码,而是字节码(bytecode).字节码是一套设计用来在Java运行时系统下执行的
高度优化的指令集,该Java运行时系统称为Java虚拟机(JavaVirtual Machine,JVM).在其标
准形式下,JVM 就是一个字节码解释器.这可能有点让人吃惊,因为像C++之类语言的编
译结果是可执行的代码.事实上,出于对性能的考虑,许多现代语言都被设计为编译型,
而不是解释型.然而,正是通过JVM运行Java程序才有助于解决在Internet上下载程序的主
要问题.这就是Java输出字节码的原因.
将一个Java程序翻译成字节码,有助于它更容易地在一个大范围的环境下运行程序.
原因非常直接:只要在各种平台上都实现Java虚拟机就可以了.在一个给定的系统中,只
要系统运行包存在,任何Java程序就可以在该系统上运行.记住:尽管不同平台的Java虚拟
机的细节有所不同,但它们都解释同样的Java字节码.如果一个Java程序被编译为本机代码,
那么对于连接到Internet上的每一种CPU类型,都要有该程序的对应版本.这当然不是一个
第1章 Java的起源 7
可行的解决方案.因此,对字节码进行解释是编写真正可移植性程序的最容易的方法.
对Java程序进行解释也有助于它的安全性.因为每个Java程序的运行都在Java虚拟机的
控制之下,Java虚拟机可以包含这个程序并且能阻止它在系统之外产生副作用.正如你将
看到的,Java语言特有的某些限制增强了它的安全性.
被解释的程序的运行速度通常确实会比同一个程序被编译为可执行代码的运行速度慢
一些.但是对Java来说,这两者之间的差别不太大.使用字节码能够使Java运行时系统的程
序执行速度比你想象的快得多.
尽管Java被设计为解释执行的程序,但是在技术上Java并不妨碍动态将字节码编译为本
机代码.SUN公司在Java 2发行版中提供了一个字节码编译器——JIT(Just In Time,即时).
JIT是Java虚拟机的一部分,它根据需要,一部分一部分地将字节码实时编译为可执行代码.
它不能将整个Java程序一次性全部编译为可执行的代码,因为Java要执行各种检查,而这些
检查只有在运行时才执行.记住这一点是很重要的,因为JIT只编译它运行时需要的代码.
尽管如此,这种即时编译执行的方法仍然使性能得到较大提高.即使对字节码进行动态编
译后,Java程序的可移植性和安全性仍能得到保证,因为运行时系统(该系统执行编译)
仍然能够控制Java程序的运行环境.不管Java程序被按照传统方式解释为字节码,还是被动
态编译为可执行代码,其功能是相同的.
1.5 Java常用语
不介绍Java常用语,对Java的总体介绍就是不完整的.尽管促使Java诞生的源动力是可
移植性和安全性,但在Java语言最终成型的过程中,其他一些因素也起了重要的作用.Java
设计开发小组的成员总结了这些关键因素,称其为Java的专门用语,包括下面几个:
简单(Simple)
安全(Secure)
可移植(Portable)
面向对象(Object-oriented)
健壮(Robust)
多线程(Multithreaded)
体系结构中立(Architecture-neutral)
解释执行(Interpreted)
高性能(High performance)
分布式(Distributed)
动态(Dynamic)
在这些特性中,安全和可移植已经在前面介绍过了,下面让我们看看其他特性的含义.
1.5.1 简单
Java的设计目的是让专业程序员觉得既易学又好用.假设你有编程经历,你将不觉得
8 第1部分 Java语言
Java难掌握.如果你已经理解面向对象编程的基本概念,学习Java将更容易.如果你是一个
经验丰富的C++程序员,那就最好了,学习Java简直不费吹灰之力.因为Java继承C/C++语
法和许多C++面向对象的特性,大多数程序员在学习Java时都不会觉得太难.另外,C++中
许多容易混淆的概念,或者被Java弃之不用了,或者以一种更清楚,更易理解的方式实现.
除了和C/C++类似以外,Java的另外一个属性也使它更容易学习:设计人员努力使Java
中不出现显得让人吃惊的特性.在Java中,很少明确地告诉你如何才能完成一项特定的任
务.
1.5.2 面向对象
尽管受到其前辈的影响,但Java没被设计成兼容其他语言源代码的程序.这允许Java
开发组自由地从零开始.这样做的一个结果是,Java语言可以更直接,更易用,更实际的
接近对象. 通过对近几十年面向对象软件优点的借鉴,Java设法在纯进化论者的"任何事
物都是一个对象"和实用主义者的"不讨论对象不对象"的论点之间找到了平衡.Java的
对象模型既简单又容易扩展,对于简单数据类型,例如整数,它保持了高性能,但不是对
象.
1.5.3 健壮
万维网上多平台的环境使得它对程序有特别的要求,因为程序必须在许多系统上可靠
地执行.这样,在设计Java时,创建健壮的程序被放到了高度优先考虑的地位.为了获得
可靠性,Java在一些关键的地方限制你,强迫你在程序开发过程中及早发现错误.同时,
Java使你不必担心引起编程错误的许多最常见的问题.因为Java是一种严格的类型语言,它
不但在编译时检查代码,而且在运行时也检查代码.事实上,在运行时经常碰到的难以重
现的,难以跟踪的许多错误在Java中几乎是不可能产生的.要知道,使程序在不同的运行
环境中以可预见的方式运行是Java的关键特性.
为更好理解Java是如何具有健壮性的,让我们考虑使程序失败的两个主要原因:内存
管理错误和误操作引起的异常情况(也就是运行时错误).在传统的编程环境下,内存管理
是一项困难,乏味的任务.例如,在C/C++中,程序员必须手工地分配并且释放所有的动
态内存.这有时会导致问题,因为程序员可能忘记释放原来分配的内存,或者释放了其他
部分程序正在使用的内存.Java通过替你管理内存分配和释放,可以从根本上消除这些问
题(事实上,释放内存是完全自动的,因为Java为闲置的对象提供内存垃圾自动收集).
在传统的环境下,异常情况可能经常由"被零除"或"文件未找到"这样的情况引起,而
我们又必须用既繁多又难以理解的一大堆指令来对它们进行管理.Java通过提供面向对象
的异常处理机制来解决这个问题.一个写得很好的Java程序,所有的运行时错误都可以并
且应该被你的程序自己进行管理.
1.5.4 多线程
设计Java的目标之一是为了满足人们对创建交互式网上程序的需要.为此,Java支持多
线程编程,因而你用Java编写的应用程序可以同时执行多个任务.Java运行时系统在多线程
第1章 Java的起源 9
同步方面具有成熟的解决方案,这使你能够创建出运行平稳的交互式系统.Java的多线程
机制非常好用,因而你只需关注程序细节的实现,不用担心后台的多任务系统.
1.5.5 结构中立
Java设计者考虑的一个主要问题是程序代码的持久性和可移植性.程序员面临的一个
主要问题是,不能保证今天编写的程序明天能否在同一台机器上顺利运行.操作系统升级,
处理器升级以及核心系统资源的变化,都可能导致程序无法继续运行.Java设计者对这个
问题做过多种尝试,Java虚拟机(JVM)就是试图解决这个问题的.他们的目标是"只要
写一次程序,在任何地方,任何时间该程序永远都能运行".在很大程度上,Java实现了
这个目标.
1.5.6 解释性和高性能
前面已提到,通过把程序编译为Java字节码这样一个中间过程,Java可以产生跨平台运
行的程序.字节码可以在提供Java虚拟机(JVM)的任何一种系统上被解释执行.早先的
许多尝试解决跨平台的方案对性能要求都很高.其他解释执行的语言系统,如BASIC,Tcl,
PERL都有无法克服的性能缺陷.然而,Java却可以在非常低档的CPU上顺利运行.前面已
解释过,Java确实是一种解释性语言,Java的字节码经过仔细设计,因而很容易便能使用JIT
编译技术将字节码直接转换成高性能的本机代码.Java运行时系统在提供这个特性的同时
仍具有平台独立性,因而"高效且跨平台"对Java来说不再矛盾.
1.5.7 分布式
Java为Internet的分布式环境而设计,因为它处理TCP/IP协议.事实上,通过URL地址
存取资源与直接存取一个文件的差别是不太大的.Java原来的版本(Oak) 包括了内置的地址
空格消息传递(intra-address-space)特性.这允许位于两台不同的计算机上的对象可以远程地
执行过程.Java最近发布了叫做远程方法调用(Remote Method Invocation ,RMI)的软件
包,这个特性使客户机/服务器编程达到了无与伦比的抽象级.
1.5.8 动态
Java程序带有多种的运行时类型信息,用于在运行时校验和解决对象访问问题.这使
得在一种安全,有效的方式下动态地连接代码成为可能,对小应用程序环境的健壮性也十
分重要,因为在运行时系统中,字节码内的小段程序可以动态地被更新.
1.6 继 续 革 命
Java的最初发布本不亚于一场革命,但是它并不标志着Java快速革新时代的结束.与大
多数其他软件系统经常进行小的改进不同,Java继续以爆炸式的步伐向前发展.在Java 1.0
发布不久,Java的设计者已经创造出了Java 1.1.Java 1.1新增的特性远比普通意义上的版本
修订有意义,内容要丰富许多.Java 1.1增加了许多新的库元素,重新定义了小应用程序处
10 第1部分 Java语言
理事件的方法,并且重新设置了1.0版中库的许多特性.它也放弃了原来由Java1.0定义的若
干过时的特征.因此,Java 1.1不但增加了Java 1.0中没有的属性,同时也抛弃了一些原有的
属性.
Java的第二个主要发布版本是Java 2.Java 2 是一个分水岭,它标志这个快速演变语言
"现代时代"的开始!Java 2第一版本的版本号是1.2.这似乎有点奇怪.原因是它参考了
原来Java库的版本,对于整个版本来说,它本身没有多大变化.Java 2增加了很多对新特性
的支持,例如Swing和类集框架,并且它提高了Java虚拟机和各种编程工具的性能.Java 2
也包含了一些不赞成继续使用的内容,主要是不赞成使用线程类中suspend( ),resume( )和
stop( )这些方法.
Java的当前版本是Java 2,1.3版.Java的这个版本是对Java 2原来版本的第一次最主要
的升级.该版本增强了Java大部分现有的功能,并且限制了它的开发环境.总的来说,版
本1.2和版本1.3的程序源代码是兼容的.尽管与前面3个版本相比,版本1.3作了一些小的改
变,但这是无关紧要的.
本书适合Java 2的1.2和1.3版.当然,大多数内容也适用于Java早期的版本.在本书中,
当一个特性只适用于Java的一个特定的版本时,会被注明.否则,你就可以认为它适用于
一般的Java版本.另外,对于适用于Java 2两个版本的那些特性,本书中将简单地使用术语
Java 2,而不注明版本号.
1.7 Java不是增强的HTML
在继续讲解前,有必要澄清一个普遍的误解.因为Java被用来创建网页,所以初学者
有时将Java与超文本标记语言(HTML)混淆,或认为Java仅仅是对HTML的一些改进.幸
好,这只是误解.实质上,HTML是一种定义信息逻辑的组织方式并提供相关信息的链接
(叫超文本链接).你可能知道,超文本链接(hypertext link)(也叫超链接)是把一个超
文本与另一个超文本文档链接起来的工具,而这个被链接的超文本文档可能在本地或万维
网上其他地方.超文本文档要素的定义是通过选择该超文本文档与另一个相关文档的链接,
在用户搜索各种路径后,该超文本文档可以非线性的方式阅读.
尽管HTML允许用户以动态方式阅读文档,但HTML永远无法成为一种编程语言.当
然,HTML确实帮助和推进了万维网的普及,HTML是促使Java诞生的催化剂,但它没有直
接从概念上影响Java语言的设计.HTML与Java的惟一联系是,HTML提供Java小应用程序
标记,该标记启动Java小应用程序.这样,就可以在超文本文档中嵌入能启动Java小应用程
序的指令.
第2章 Java语言概述
像所有其他的计算机语言一样,Java的各种要素不是独立存在的,它们作为一个整体
共同构成了Java语言.这种关联使得不讲其他方面而单独描述Java的某一方面是困难的.讨
论一个特性经常要先具有另外一个特性的知识.因此,本章先对Java的若干主要特性做简
单综述.这里描述的主题将给你一个立足点:能够使你编写和理解简单的Java程序.大多
数讨论话题将在第1部分的其他章节详细叙述
2.1 面向对象编程
Java的核心是面向对象编程.事实上,所有的Java程序都是面向对象的,你别无选择.
这一点与C++不同,因为在那里你可以选择是否面向对象编程.面向对象编程与Java密不可
分,因此,在你编写哪怕是最简单的Java程序以前,也必须理解它的基本原则.因此,本
章先从面向对象编程的概念讲起.
2.1.1 两种范型
我们知道,所有的计算机程序都由两类元素组成:代码和数据.此外,从概念上讲,
程序还可以以它的代码或是数据为核心进行组织编写.也就是说,一些程序围绕"正在发
生什么"编写,而另一些程序则围绕"谁将被影响"编写.这两种范型决定程序的构建方
法.第一种方法被称为面向过程的模型(process-oriented model),用它编写的程序都具有
线性执行的特点.面向过程的模型可认为是代码作用于数据,像C这样的过程式语言采用
这个模型是相当成功的.然而,正如在第1章提到的,当程序变得更大并且更复杂时,就会
出现问题.
为了管理不断增加的复杂性,第二种方式,也就是面向对象的编程(object-oriented
programming)被构思出来了.面向对象的编程围绕它的数据(即对象)和为这个数据严格
定义的接口来组织程序.面向对象的程序实际上是用数据控制对代码的访问.下面你将看
到,将控制的实体变换为数据,可使程序在组织结构上从若干方面受益.
2.1.2 抽象
面向对象编程的一个实质性的要素是抽象.人们通过抽象(abstraction)处理复杂性.
例如,人们不会把一辆汽车想象成由几万个互相独立的部分所组成的一套装置,而是把汽
车想成一个具有自己独特行为的对象.这种抽象使人们可以很容易地将一辆汽车开到杂货
店,而不会因组成汽车各部分零件过于复杂而不知所措.他们可以忽略引擎,传动及刹车
系统的工作细节,将汽车作为一个整体来加以利用.
使用层级分类是管理抽象的一个有效方法.它允许你根据物理意义将复杂的系统分解
12 第1部分 Java语言
为更多更易处理的小块.从外表看,汽车是一个独立的对象.一旦到了内部,你会看到汽
车由若干子系统组成:驾驶系统,制动系统,音响系统,安全带,供暖,便携电话,等等.
再进一步细分,这些子系统由更多的专用元件组成.例如,音响系统由一台收音机,一个
CD播放器,或许还有一台磁带放音机组成.从这里得到的重要启发是,你通过层级抽象对
复杂的汽车(或任何另外复杂的系统)进行管理.
复杂系统的分层抽象也能被用于计算机程序设计.传统的面向过程程序的数据经过抽
象可用若干个组成对象表示,程序中的过程步骤可看成是在这些对象之间进行消息收集.
这样,每一个对象都有它自己的独特行为特征.你可以把这些对象当作具体的实体,让它
们对告诉它们做什么事的消息作出反应.这是面向对象编程的本质.
面向对象的概念是Java 的核心,对程序员来讲,重要的是要理解这些概念怎么转化为
程序.你将会发现,在任何主要的软件工程项目中,软件都不可避免地要经历概念提出,
成长,衰老这样一个生命周期,而面向对象的程序设计,可以使软件在生命周期的每一个
阶段都处变不惊,有足够的应变能力.例如,一旦你定义好了对象和指向这些对象的简明
的,可靠的接口,你就能很从容很自信地解除或更替旧系统的某些组成部分.
2.1.3 面向对象编程的3个原则
所有面向对象的编程语言都提供帮助你实现面向对象模型的机制,这些机制是封装,
继承及多态性.现在让我们来看一下它们的概念.
封装
封装(Encapsulation)是将代码及其处理的数据绑定在一起的一种编程机制,该机制
保证了程序和数据都不受外部干扰且不被误用.理解封装性的一个方法就是把它想成一个
黑匣子,它可以阻止在外部定义的代码随意访问内部代码和数据.对黑匣子内代码和数据
的访问通过一个适当定义的接口严格控制.如果想与现实生活中的某个事物作对比,可考
虑汽车上的自动传送.自动传送中包含了有关引擎的数百比特的信息,例如你正在以什么
样的加速度前进,你行驶路面的坡度如何,以及目前的档位.作为用户,你影响这个复杂
封装的方法仅有一个:移动档位传动杆.例如,你不能通过使用拐弯信号或挡风玻璃擦拭
器影响传动.所以档位传动杆是把你和传动连接起来的惟一接口.此外,传动对象内的任
何操作都不会影响到外部对象,例如,档位传动装置不会打开车前灯!因为自动传动被封
装起来了,所以任何一家汽车制造商都可以选择一种适合自己的方式来实现它.然而,从
司机的观点来看,它们的用途都是一样的.与此相同的观点能被用于编程.封装代码的好
处是每个人都知道怎么访问它,但却不必考虑它的内部实现细节,也不必害怕使用不当会
带来负面影响.
Java封装的基本单元是类.尽管类将在以后章节详细介绍.现在仍有必要对它作一下
简单的讨论.一个类(class)定义了将被一个对象集共享的结构和行为(数据和代码).
一个给定类的每个对象都包含这个类定义的行为和结构,好像它们是从同一个类的模子中
铸造出来似的.因为这个原因,对象有时被看作是类的实例(instances of a class).所以,
类是一种逻辑结构,而对象是真正存在的物理实体.
当创建一个类时,你要指定组成那个类的代码和数据.从总体上讲,这些元素都被称
第2章 Java语言概述 13
为该类的成员(members).具体地说,类定义的数据称为成员变量(member variables)
或实例变量(instance variables).操作数据的代码称为成员方法(member methods)或简
称方法(methods).如果你对C/C++熟悉,可以这样理解:Java程序员所称的方法,就是
C/C++程序员所称的函数(function).在完全用Java编写的程序中,方法定义如何使用成
员变量.这意味着一个类的行为和接口是通过方法来定义的,类这些方法对它的实例数据
进行操作.
既然类的目的是封装复杂性,在类的内部就应该有隐藏实现复杂性机制.类中的每个
方法或变量都可以被标记为私有(private)或公共(public).类的公共接口代表类的外部
用户需要知道或可以知道的每件事情;私有方法和数据仅能被一个类的成员代码所访问,
其他任何不是类的成员的代码都不能访问私有的方法或变量.既然类的私有成员仅能被程
序中的其他部分通过该类的公共方法访问,那么你就能保证不希望发生的事情就一定不会
发生.当然,公共接口应该小心仔细设计,不要过多暴露类的内部内容(见图2-1).
图2-1 封装:可用来保护私有数据的公共方法
继承
继承(Inheritance)是一个对象获得另一个对象的属性的过程.继承很重要,因为它支
持了按层分类的概念.如前面提到的,大多数知识都可以按层级(即从上到下)分类管理.
例如,尊贵的猎犬是狗类的一部分,狗又是哺乳动物类的一部分,哺乳动物类又是动物类
的一部分.如果不使用层级的概念,我们就不得不分别定义每个动物的所有属性.使用了
继承,一个对象就只需定义使它在所属类中独一无二的属性即可,因为它可以从它的父类
那儿继承所有的通用属性.所以,可以这样说,正是继承机制使一个对象成为一个更具通
用类的一个特定实例成为可能.下面让我们更具体地讨论这个过程.
大多数人都认为世界是由对象组成的,而对象又是按动物,哺乳动物和狗这样的层级
结构相互联系的.如果你想以一个抽象的方式描述动物,那么你可以通过大小,智力及骨
14 第1部分 Java语言
胳系统的类型等属性进行描述.动物也具有确定的行为,它们也需要进食,呼吸,并且睡
觉.这种对属性和行为的描述就是对动物类的定义.
如果你想描述一个更具体的动物类,比如哺乳动物,它们会有更具体的属性,比如牙
齿类型,乳腺类型等.我们说哺乳类动物是动物的子类(subclass),而动物是哺乳动物的
超类(superclass).
由于哺乳动物类是需要更加精确定义的动物,所以它可以从动物类继承(inherit)所有
的属性.一个深度继承的子类继承了类层级(class hierarchy)中它的每个祖先的所有属性.
继承性与封装性相互作用.如果一个给定的类封装了一些属性,那么它的任何子类将
具有同样的属性,而且还添加了子类自己特有的属性(见图2-2).这是面向对象的程序在
复杂性上呈线性而非几何性增长的一个关键概念.新的子类继承它的所有祖先的所有属性.
它不与系统中其余的多数代码产生无法预料的相互作用.
多态性
多态性(Polymorphism,来自于希腊语,表示"多种形态")是允许一个接口被多个
同类动作使用的特性,具体使用哪个动作与应用场合有关,下面我们以一个后进先出型堆
栈为例进行说明.假设你有一个程序,需要3种不同类型的堆栈.一个堆栈用于整数值,一
个用于浮点数值,一个用于字符.尽管堆栈中存储的数据类型不同,但实现每个栈的算法
是一样的.如果用一种非面向对象的语言,你就要创建3个不同的堆栈程序,每个程序一个
名字.但是,如果使用Java,由于它具有多态性,你就可以创建一个通用的堆栈程序集,
它们共享相同的名称.
多态性的概念经常被说成是"一个接口,多种方法".这意味着可以为一组相关的动
作设计一个通用的接口.多态性允许同一个接口被必于同一类的多个动作使用,这样就降
低了程序的复杂性.选择应用于每一种情形的特定的动作(specific action)(即方法)是
编译器的任务,程序员无需手工进行选择.你只需记住并且使用通用接口即可.
第2章 Java语言概述 15
图2-2 拉不拉多猎犬继承所有其超类的封装
再拿狗作比喻,一条狗的嗅觉是多态的.如果狗闻到猫的气味,它会在吠叫并且追着
它跑.如果狗闻到食物的气味,它将分泌唾液并向盛着食物的碗跑去.两种状况下是同一
种嗅觉器官在工作,差别在于闻到了什么气味,也就是有两种不同类型的数据作用于狗的
鼻子!在一个Java程序中使用方法时,也可以采用这个通用的概念.
多态性,封装性与继承性相互作用
如果用得当,在由多态性,封装性和继承性共同组成的编程环境中可以写出比面向过
程模型环境更健壮,扩展性更好的程序.精心设计的类层级结构是重用你花时间和努力改
进并测试过的程序的基础,封装可以使你在不破坏依赖于类公共接口的代码基础上对程序
进行升级迁移,多态性则有助于你编写清楚,易懂,易读,易修改的程序.
在前面两个与现实生活有关的实例中,汽车更能全面说明面向对象设计的优点,为介
绍继承而用狗作类比也很有趣.总的来说,汽车与程序很相似,所有的驾驶员依靠继承性
很快便能掌握驾驶不同类型(子类)车辆的技术.不管是接送学生的校车,或是默西迪斯
16 第1部分 Java语言
私家轿车,或是保时捷汽车,或是家庭汽车,司机差不多都能找到方向盘,制动闸和加速
器,并知道如何操作.经过一段驾驶,大多数人甚至能知道手动档与自动档之间的差别,
因为他们从根本上理解这两个档的超类——传动.
人们在汽车上看见的总是封装好的特性.刹车和踏脚板隐蔽着不可思议的复杂性,但
接口却是如此简单,你的脚就可以操作它们!引擎,制动闸及轮胎的大小对于你如何定义
踏脚板类的接口没有任何影响.
最后的属性,多态性,在汽车制造商基于相同的交通工具所提供的多种选择的能力上
得到了充分反映.例如,刹车系统有正锁和反锁之分,方向盘有带助力或不带助力之分,
引擎有4缸,6缸或8缸之分.无论设置如何,你都得脚踩刹车板来停车,转动方向盘来转向,
按离合器来制动.同样的接口能被用来控制许多不同的实现过程.
正如你所看到的,通过封装,继承及多态性原理,各个独立部分组成了汽车这个对象.
这在计算机程序设计中也是一样的.通过面向对象原则的使用,可以把程序的各个复杂部
分组合成一个一致的,健壮的,可维护的程序整体.
正如本节开始时提到的,所有的Java程序都是面向对象的.或者,更精确地说,每个
Java程序都具有封装性,继承性及多态性.尽管在本章将要介绍的简单示例程序及以后几
章的示例程序中并未体现所有这些特性,但也有所体现.你将看到,Java提供的许多特性
是它的内置类库的一部分,这个库使封装性,继承性及多态性得到更广泛应用.
2.2 第1个简单程序
既然Java面向对象的基础已经被讨论过了,接下来让我们看一些实际的Java程序.让我
们从编译及运行下面这个简短示例程序开始.你将看到,这个程序的功能比你想像的要多.
/*
This is a simple Java program.
Call this file "Example.java".
*/
class Example {
// Your program begins with a call to main().
public static void main(String args[]) {
System.out.println("This is a simple Java program.");
}
}
注意:在下面的介绍中,将使用标准JDK(Java Developer's Kit,Java 开发工具
包),它可从Sun Microsystems公司得到.如果你正在使用其他的Java开发环境,
则Java程序编译,运行过程可能有所不同.在这种情况下,请你查阅编译器的用户
手册来获得具体指导.
2.2.1 键入程序
对大多数计算机语言,包含程序源代码的文件名是任意的,但对于Java就不行.关于
Java,你需要知道的第一件事就是源文件的名字非常重要.对这个例子,源程序文件名应
第2章 Java语言概述 17
该是Example.java.下面我们将解释其中的原因.
在Java中,一个源程序文件被称为一个编译单元(compilation unit).它是一个包含一
个或多个类定义的文本文件.Java编译器要求源程序文件使用.java文件扩展名.请注意,
文件扩展名长度是4个字符.所以,你的操作系统一定要有支持长文件名的能力.这意味着
DOS和Windows 3.1是不支持Java的(至少在目前是这样).当然,它可在Windows 95/98和
Windows NT/2000下正常工作.
从上述示例程序中可以看出,程序中定义的类名也是Example.这不是巧合.在Java中,
所有的代码都必须驻留在类中.按照约定,类名必须与源程序的文件名相同.你也应该确
保文件名的大小写字母与类名一样,这是因为Java是区分大小写的.虽然文件名与类名必
须一致的约定似乎有点专制,但是这个约定有助于你轻松地维护及组织程序.
2.2.2 编译程序
要编译示例程序Example,须运行编译器程序javac,并在命令行上指定源程序文件名,
格式如下所示:
C:\>javac Example.java
编译器javac产生了一个名为Example.class的文件,该文件包含程序的字节码.前面已
讨论过,Java字节码中包含的是Java解释程序将要执行的指令码.因此,javac的输出并不
是可以直接运行的代码.
要真正运行该程序,你必须使用名叫java的Java解释器.具体做法是把类名Example作
为一个命令行参数输入,格式如下所示:
C:\>java Example
运行这个程序,将输出如下内容:
This is a simple Java program.
当Java源代码被编译后,每个单独的类都被放入自己的输出文件中,并以类的名字加
".class"扩展名为其文件名.这就是为什么Java源程序文件必须与其中包含的类同名的原
因——源程序文件将与".class"文件相同.运行Java解释器实际上是指定你想要解释器运
行的类的名字,它会自动搜索包含该名字且带有.class扩展名的文件.如果找到,它将运行
包含在该指定类中的代码.
2.2.3 详细讨论第1个示例程序
尽管Example.java很短,但它包括了所有Java程序具有的几个关键特性.让我们仔细分
析该程序的每个部分.
程序开始于以下几行:
/*
This is a simple Java program.
Call this file "Example.java".
*/
18 第1部分 Java语言
这是一段注释(comment).像大多数其他的编程语言一样,Java也允许你在源程序文
件中加注释.注释中的内容将被编译器忽略.事实上,注释是为了给任何阅读源代码程序
的人说明或解释程序的操作.在本例中,注释对程序进行说明,并提醒你该源程序的名字
叫做Example.java.当然,在真正的应用中,注释通常用来解释程序的某些部分如何工作或
某部分的特殊功能.
Java支持3种类型的注释.在示例程序顶部的注释称为多行注释(multiline comment).
这类注释开始于"/*",结束于"*/".这两个注释符间的任何内容都将被编译器忽略.正
如"多行注释"名字所示,一个多行注释可以包含若干行文字.
程序的下一行代码如下所示:
class Example {
该行使用关键字class声明了一个新类,Example是类名标识符,整个类定义(包括其所
有成员)都将位于一对花括号({})之间,花括号在Java中的使用方式与C或C++相同,目
前,不必考虑类的细节,只是有一点要注意,在Java中,所有程序活动都发生在类内,这
就是为什么Java程序是面向对象的.
下面一行程序是单行注释:
// Your program begins with a call to main().
这是Java支持的第二种类型的注释.单行注释(single-line comment)始于"//",在该
行的末尾结束.通常情况下,程序员们对于较长的注释使用多行注释,而对于简短的,一
行一行的注释则使用单行注释.
下一行代码如下所示:
public static void main(String args[]) {
该行开始于main( )方法.正如它前面的注释所说,这是程序将要开始执行的第一行.
所有的Java应用程序都通过调用main( )开始执行(这一点同C/C++一样),我们在此还不能
对该行的每一个部分作出精确的解释,因为这需要详细了解Java封装性的特点,但是,由
于本书第1部分中的大多数例子都用到这一行代码,我们将对各部分作一个简单介绍.
关键字public是一个访问说明符(access specifier),它允许程序员控制类成员的可见
性.如果一个类成员前面有public,则说明该成员能够被声明它的类之外的代码访问(与
public相对的是private,它禁止成员被所属类之外的代码访问).在本例中,main( )必须被
定义为public类型,因为当程序开始执行时它需要被它的类之外的代码调用.关键字static
允许调用main( )而不必先实现该类的一个特殊实例.这是必要的,因为在任何对象被创建
之前,Java解释器都会调用main().关键字void仅通知编译器main()不返回任何值.你将看
到,方法也可以有返回值.如果这一切似乎有一点令人费解,别担心.所有这些概念都将
在随后的章节中详细讨论.
前面已经介绍过,main()是Java程序开始时调用的方法.请记住,Java是区分大小写的.
因此,main与Main是不同的.Java编译器也可以编译不包含main()方法的类,但是Java解释
程序没有办法运行这些类.因此,如果你输入了Main而不是main,编译器仍将编译你的程
序,但Java解释程序将报告一个错误,因为它找不到main()方法.
第2章 Java语言概述 19
你要传递给方法的所有信息由方法名后面括号中指定的变量接收,这些变量被称为参
数(parameters).即使一个方法不需要参数,你仍然需要在方法名后面放置一对空括号.
在main()中,只有一个参数,即String args[],它声明了一个叫做args的参数,该参数是String
类的一个实例数组(注:数组是简单对象的集合).字符串类型的对象存储字符的串.在
本例中,args接收程序运行时显示的任何命令行参数.本例中的这个程序并没有使用这些
信息,但是本书后面讲到的其他一些程序将使用它们.
该行的最后一个字符是"{".它表示了main()程序体的开始.一个方法中包含的所有
代码都将包括在这对花括号中间.
另外,main( )仅是解释器开始工作的地方.一个复杂的程序可能包含几十个类,但这
些类仅需要一个main( )方法以供解释器开始工作.当你开始引用被嵌入在浏览器中的Java
小应用程序时,你根本不用使用main( )方法,因为Web浏览器使用另一种不同的方法启动
小应用程序.
接下来的代码行如下所示.请注意,它出现在main( )内.
System.out.println("This is a simple Java program.");
本行在屏幕上输出字符串"This is a simple Java program.",输出结果后面带一个空行.
输出实际上是由内置方法 println ( )来实现的.在本例中,println ( )显示传递给它的字符串.
你将会看到,println ( )方法也能用来显示其他类型的信息.该行代码开始于System.out,现
在对它作详细说明为时尚早,需涉及很多复杂内容.简单的说,System是一个预定义的可
访问系统的类,out是连接到控制台的输出流.
可能你已经猜到了,控制台输出(输入)在实际的Java程序和小应用程序中并不经常
使用.因为绝大多数现代计算环境从本质上讲都是窗口和图形界面的,控制台I/O主要被用
简单的实用工具程序和演示程序使用.在本书后面,你将会学到用Java生成输出的其他方
法.但是目前,我们将继续使用控制台I/O方法.
请注意,println( )语句以一个分号结束.在Java中,所有的语句都以一个分号结束.该
程序的其他行没有以分号结束,这是因为从技术上讲,它们并不是程序语句.
程序中的第一个"}"号结束了main( ),而最后一个"}"号结束类Example的定义.
2.3 第2个示例程序
对于编程语言来说,变量是一个最为基本的概念.你可能知道,变量是一个有名字的
内存位置,它能够被赋值.而且,在程序的运行过程中,变量的值是可以改变的.下一个
程序将介绍如何声明变量,如何给变量赋值.另外,该程序也说明了控制台输出的某些新
特点.从程序开始的注释可以看出,你应该把这个文件命名为Example2.java.
/*
Here is another short example.
Call this file "Example2.java".
*/
class Example2 {
public static void main(String args[]) {
20 第1部分 Java语言
int num; // this declares a variable called num
num = 100; // this assigns num the value 100
System.out.println("This is num: " + num);
num = num * 2;
System.out.print("The value of num * 2 is ");
System.out.println(num);
}
}
运行该程序时,你将会看到如下的运行结果:
This is num: 100
The value of num * 2 is 200
让我们来进一步查看这个结果是如何产生的.我们重点考虑与前一示例不同的代码,
在上一个程序中未出现的第一行代码是:
int num; // this declares a variable called num
该行声明了一个名为num的整型变量.和其他大多数语言一样,在Java中一定要先声明
变量,然后再使用变量.
下面是声明变量的一般形式:
type var-name;
在这里,type表示所要声明的变量的类型,var-name是所要声明变量的名称.如果你要
声明多个属于同一类型的变量,只需用逗号将各个变量名分开即可.Java定义了几种数据
类型:整型(integer),字符型(character),浮点型(floating-point).关键字int指的是
整数类型.
在程序中,下面这一行代码将100赋予变量num.
num = 100; // this assigns num the value 100
在Java中,赋值符号是等号.
下面的这行程序在输出变量值之前,先输出字符串"This is num:".
System.out.println("This is num: " + num);
在这个语句中,变量num之前的加号"+"的作用是,让num的取值与它前面的字符串
相连接,然后再输出结果字符串的内容(实际上,变量num先被它赋值再超值转换成字符
串,然后再和加号之前的字符串相连接.这个过程将在本书的后面详细讨论).这种方法
可以被推广.通过加号"+"的连接操作,你可以在println()这个方法之内将尽可能多的
字符串内容连在一起.
接下来的语句行将变量num乘2以后的结果重新赋值给变量num.和其他大多数语言一
样,Java用"*"符号来表示乘法运算.在执行这行语句之后,变量num的值变成了200.
本程序接下来的两行代码是:
第2章 Java语言概述 21
System.out.print("The value of num * 2 is ");
System.out.println(num);
在这两行中有几个新内容.首先,内置方法print( )被用来显示字符串"The value of num
* 2 is".该字符串后面不换行,这意味着如果生成第二个输出,它将在同一行中开始输出.
方法print ( )和方法println ( )类似,只是它在每次调用后并不输出一个新行(即换行).其
次,在调用println ( )时,注意变量num可以被自身使用.方法print ( )和方法println ( )都能够
用来输出Java的任何内置类型的值.
2.4 两个控制语句
尽管将在第5章仔细讨论控制语句,我们还是在这里先简单介绍2条控制语句,以便能
在第3章,第4章中的例子程序中使用它们,并且它们也将帮助说明Java的一个重要特点:
程序块.
2.4.1 if控制语句
Java中if控制语句与其他语言中的IF语句非常相似.并且,它与C/ C++语言中的if语句
的语法完全相同.它的最简单形式如下:
if(condition) statement;
这里,条件condition是一个布尔型表达式.如果条件为真,那么执行语句statement;如
果条件为假,则语句statement将被绕过而不被执行.下面是一个例子:
if(num < 100) println("num is less than 100");
在这个例子中,如果变量num的值小于100,那么条件表达式的值为真,方法 println ( )
将被调用执行.如果变量num的值大于或等于100,那么方法println ( )被绕过而不被执行 .
在第4章,中你将看到Java在条件语句中用到的所有的关系运算符,下面是其中一部分:
运算符 含 义
大于
== 等于
注意,判断是否相等的关系运算符是两个等号"==".
下面的程序说明了if控制语句的用法:
/*
Demonstrate the if.
Call this file "IfSample.java".
*/
class IfSample {
public static void main(String args[]) {
22 第1部分 Java语言
int x,y;
x = 10;
y = 20;
if(x y) System.out.println("x now greater than y");
// this won't display anything
if(x == y) System.out.println("you won't see this");
}
}
该程序产生的结果如下所示:
x is less than y
x now equal to y
x now greater than y
这个程序中另一个需要注意的地方是:
int x ,y ;
该程序行使用逗号来分隔变量列表,定义了2个变量x和y.
2.4.2 for循环
你可能从先前的编程经验已经知道,在几乎所有的编程语言中,循环语句都是其重要
组成部分.Java也不例外.事实上,你将在第5章中看到,Java提供了一套功能强大的循环
结构.For循环也许是最通用的.如果你对C或C++熟悉,那么你应该感到高兴,因为Java
的for循环和其他语言中的for循环操作完全一样.如果你不熟悉C/C++,for循环也是容易使
用的.最简单的for循环结构如下所示:
for(initialization; condition; iteration) statement;
在这个最常见的形式中,循环体的初始化部分(initialization)设置循环变量并为变量
赋初始值.条件判断部分(condition)是测试循环控制变量的布尔表达式.如果测试的结
果为真,循环体(statement)继续反复执行;如果测试的结果为假,循环结束.迭代部分
(iteration)的表达式决定循环控制变量在每次循环后是如何改变的.下面的短程序说明了
for循环的使用方法:
/*
Demonstrate the for loop.
Call this file "ForTest.java".
*/
class ForTest {
public static void main(String args[]) {
第2章 Java语言概述 23
int x;
for(x = 0; x<10; x = x+1)
System.out.println("This is x: " + x);
}
}
这个程序产生的结果如下:
This is x: 0
This is x: 1
This is x: 2
This is x: 3
This is x: 4
This is x: 5
This is x: 6
This is x: 7
This is x: 8
This is x: 9
在这个例子中,x是循环控制变量.它在for的初始化部分被初始化为零.在每次重复
迭代(包括第一次)的开始,执行条件测试x< 10.如果测试的结果为真,println ( )语句被
执行,然后执行循环体的迭代部分.这个过程将持续进行下去,直到条件测试的结果为假.
有趣的是,在Java专业程序员编写的程序中,循环体的迭代部分一般不会像前面程序
示例那样.即你很少会看到下面的语句:
x = x + 1;
原因是Java有一个特殊的增量运算符,能够更高效地执行这项操作.该增量运算符是
"++"(即2个加号).递增运算符每次使其作用对象加1.通过使用递增运算符,上条语句
可以这样写:
x++;
这样,前述的for循环语句通常写成这样:
for(x = 0; x<10; x++)
你可以将上一个程序的for循环语句改写成这样试一下.你将看到,运行结构与以前相
同.
Java也提供一个递减运算符:"--"(即2个减号).递减运算符使其作用对象每次减1.
2.5 使用程序块
在Java中,可以将2个或2个以上的语句组成一组,这样的一组语句称为程序块
(Codeblocks).程序块是通过将所属语句放在花括号中来实现.一旦创建了程序块,
它就成为一个逻辑单元,可以作为一个单独的语句来使用.例如,程序块可以作为Java
中if控制语句和for控制语句的目标.我们来看一看下面的if控制语句:
24 第1部分 Java语言
if(x < y) { // begin a block
x = y;
y = 0;
} // end of block
本例中,如果x小于y,那么在程序块内的两条语句都将执行.因此,程序块中的这2
条语句组成一个逻辑单元,不能一条语句运行,而另一条语句不运行.其中的关键一点是
如果你需要将两个或多个语句在逻辑上连接起来 ,你就可以将其放入一个程序块中.
让我们看另外的例子.下面的程序将for循环作为一个程序块使用.
/*
Demonstrate a block of code.
Call this file "BlockTest.java"
*/
class BlockTest {
public static void main(String args[]) {
int x,y;
y = 20;
// the target of this loop is a block
for(x = 0; x<10; x++) {
System.out.println("This is x: " + x);
System.out.println("This is y: " + y);
y = y - 2;
}
}
}
这个程序产生的结果如下所示:
This is x: 0
This is y: 20
This is x: 1
This is y: 18
This is x: 2
This is y: 16
This is x: 3
This is y: 14
This is x: 4
This is y: 12
This is x: 5
This is y: 10
This is x: 6
This is y: 8
This is x: 7
This is y: 6
This is x: 8
This is y: 4
This is x: 9
This is y: 2
在本例中,for循环作为一个程序块使用,而并不是一个单独的语句.这样,每循环一
第2章 Java语言概述 25
次,块内的3条语句都要运行一次.这个事实当然被程序的执行结果证实了.
在本书的后面,你会看到程序块的其他性质和用法.当然,它们存在的主要原因是为
了创建逻辑上独立的代码单元.
2.6 基 本 词 汇
既然你已经看过了几个短的Java程序,现在让我们更正式的介绍Java的基本元素.Java
程序由空白分隔符,标识符,注释,文字,运算符,分隔符,以及关键字组成.运算符将
在下一章详细讨论,本节讨论其他的元素.
2.6.1 空白分隔符(whitespace)
Java 是一种形式自由的语言.这意味着你不需要遵循任何特殊的缩进书写规范.例如,
例子程序的所有代码都可以在一行上,你也可以按自己喜欢的方式输入程序代码,前提是
必须在已经被运算符或分隔符描述的标记之间至少留出一个空白分隔符.在Java中,空白
分隔符可以是空格,Tab跳格键或是换行符.
2.6.2 标识符(identifiers)
标识符是赋给类,方法或是变量的名字.一个标识符可以是大写和小写字母,数字,
下划线,美元符号的任意顺序组合,但不能以一个数字开始.否则容易与数字,常量相混
淆.再次强调一下,Java是区分大小写的,VALUE和Value是两个不同的标识符.下面是一
些有效的标识符:
AvgTemp count a4 $test this_is_ok
下面是一些无效的变量名:
2count high-temp Not/ok
2.6.3 常量(literal)
在Java中,常量用literal表示.例如,下面是一些常量:
100 98.6 'X' "This is a test"
从左到右,第一个表示一个整数,第二个是浮点值,第三个是一个字符常数,最后是
一个字符串.常量能在任何地方被它所允许的类型使用,代表的是所属类型的一个值.
2.6.4 注释(comments)
Java定义了3种注释的类型.其中2种注释类型你已经知道了:单行注释和多行注释.
第3种注释类型被称为文档注释(documentation comment).这类注释以HTML文件的形式
为你的程序作注释.文档注释以"/**"开始,以"*/"结束.在附录A中对文档注释作了
解释.
26 第1部分 Java语言
2.6.5 分隔符(separators)
在Java中 ,有一些字符被当作分隔符使用,最常用的分隔符是分号(;),用来分隔语
句.下面是常用的分隔符.
符 号 名称 用途
() 圆括号 在定义和调用方法时用来容纳参数表.在控制语句或强制类型转换组成
的表达式中用来表示执行或计算的优先权
{ } 花括号,大括号 用来包括自动初始化的数组的值.也用来定义程序块
对于计算机语言的发展史,业界一般认为:B语言导致了C语言的诞生,C语言演变出
了C++语言,而C++语言将让位于Java语言.要想更好地了解Java语言,就必须了解它产生
的原因,推动它发展的动力,以及它对其他语言的继承.像以前其他成功的计算机语言一
样,Java继承了其他语言的先进原理,同时又因其独特的环境要求而提出了一些创新性的
概念.在这本书的其他各章中,将从实用的角度,对Java语言,库及应用程序进行包括语
法在内的详细介绍.在本章里,我们将介绍Java语言产生的背景,发展过程,以及使它变
得如此重要的原因.
尽管Java语言已和Internet的在线环境密不可分,但首先应该注意到的最重要一点是:
它是一种程序语言.计算机语言的革新和发展需要2个基本因素的驱动:
适应正在变化的环境和需求
实现编程艺术的完善与提高
下面你将看到,Java也正是在这两个因素的驱动下产生的.
1.1 Java的由来
Java总是和C++联系在一起,而C++则是从C语言派生而来的,所以Java语言继承了这
两种语言的大部分特性.Java的语法是从C继承的,Java许多面向对象的特性受到C++的影
响.事实上,Java中几个自定义的特性都来自于或可以追溯到它的前驱.而且,Java语言的
产生与过去30年中计算机语言细致改进和不断发展密切相关.基于这些原因,本节将按顺
序回顾促使Java产生的事件和推动力.正如你将看到的一样,每一次语言设计的革新都是
因为先前的语言不能解决目前遇到的基本问题而引起.Java也不例外.
1.1.1 现代的编程语言的诞生:C语言
C语言的产生震撼了整个计算机界.它的影响不应该被低估,因为它从根本上改变了
编程的方法和思路.C语言的产生是人们追求结构化,高效率,高级语言的直接结果,可
用它替代汇编语言开发系统程序.当设计一种计算机语言时,经常要从以下几方面进行权
衡:
第1部分 Java语言
Title:Java2参考大全(第四版)
Description:Java2:The Complete Reference
Copyright:Copyright (c) 2001 by The McGraw-Hill Companies.
Author:Herbert Schildt
Version:Fourth Edition
Editor:Windflowers
2 第1部分 Java语言
易用性与功能
安全性和效率性
稳定性和可扩展性
C语言出现以前,程序员们不得不经常在有优点但在某些方面又有欠缺的语言之间做
出选择.例如,尽管公认FORTRAN在科学计算应用方面可以编写出相当高效的程序,但
它不适于编写系统程序.BASIC虽然容易学习,但功能不够强大,并且谈不上结构化,这
使它应用到大程序的有效性受到怀疑.汇编语言虽能写出高效率的程序,但是学习或有效
地使用它却是不容易的.而且,调试汇编程序也相当困难.
另一个复杂的问题是,早期设计的计算机语言(如BASIC,COBOL,FORTRAN等)
没有考虑结构化设计原则,使用GOTO语句作为对程序进行控制的一种主要方法.这样做
的结果是,用这些语言编写的程序往往成了"意大利面条式的程序代码",一大堆混乱的
跳转语句和条件分支语句使得程序几乎不可能被读懂.Pascal虽然是结构化语言,但它的设
计效率比较低,而且缺少几个必需的特性,因而无法在大的编程范围内使用(特别是,给
定的Pascal的标准语言在特定时间是可用的,但将Pascal作为系统级编码是不切实际的).
因此,在C语言产生以前,没有任何一种语言能完全满足人们的需要,但人们对这样
一种语言的需要却是迫切的.在20世纪70年代初期,计算机革命开始了,对软件的需求量
日益增加,使用早期的计算机语言进行软件开发根本无法满足这种需要.学术界付出很多
努力,尝试创造一种更好的计算机语言.但是,促使C语言诞生的另一个,也许是最重要
的因素,是计算机硬件资源的富余带来了机遇.计算机不再像以前那样被紧锁在门里,程
序员们可以随意使用计算机,可以随意进行自由尝试,因而也就有了可以开发适合自己使
用的工具的机会.所以,在C语言诞生的前夕,计算机语言向前飞跃的时机已经成熟.
在Dennis Ritchie第一个发明和实现在DEC PDP-11上运行UNIX操作系统时,一种更古
老的由Martin Richards设计的BCPL语言导致了C语言的产生.受BCPL语言的影响,由Ken
Thompson发明的B语言,在20世纪70年代逐渐向C语言发展演变.在此后的许多年里,由
Brian Kernighan和Dennis Ritchie编写的《The C Programming Language》(Prentice-Hall,1978)
被认为是事实上的C语言标准,该书认为C只是支持UNIX 操作系统的一种语言.1989年12
月,美国国家标准化组织( ANSI )制定了C语言的标准,C语言被正式标准化.
许多人认为C语言的产生标志着现代计算机语言时代的开始.它成功地综合处理了长
期困扰早期语言的矛盾属性.C语言是功能强大,高效的结构化语言,简单易学,而且它
还包括一个无形的方面:它是程序员自己的语言.在C语言出现以前,计算机语言要么被
作为学术实验而设计,要么由官僚委员会设计.而C语言不同.它的设计,实现,开发由
真正的从事编程工作的程序员来完成,反映了现实编程工作的方法.它的特性经由实际运
用该语言的人们不断去提炼,测试,思考,再思考,使得C语言成为程序员们喜欢使用的
语言.确实,C语言迅速吸引了许多狂热的追随者,因而很快受到许多程序员的青睐.简
言之,C语言是由程序员设计并由他们使用的一种语言.正如你将看到的,Java继承了这个
思想.
第1章 Java的起源 3
1.1.2 对C++的需要
在20世纪70年代末和80年代初,C成为了主流的计算机编程语言,至今仍被广泛使用.
你也许会问,既然C是一种成功且有用的语言,为什么还需要新的计算机语言 答案是复
杂性(complexity).程序越来越复杂这一事实贯穿编程语言的历史.C++正是适应了这一
需求.下面介绍为什么对程序复杂性的更好管理是C++产生的基本条件.
自从计算机发明以来,编程方法经历了戏剧性的变化.例如,当计算机刚发明出来时,
编程是通过面板触发器用人工打孔的办法输入二进制机器指令来实现的.对于只有几百行
的程序,这种办法是可行的.随着程序不断增大,人们发明了汇编语言,它通过使用符号
来代替机器指令,这样程序员就能处理更大,更复杂的程序.随着程序的进一步增大,高
级语言产生了,它给程序员提供了更多的工具来处理复杂性问题.
第一个被广泛使用的高级语言当然是FORTRAN.尽管FORTRAN最初给人留下了深刻
的印象,但它无法开发出条理清楚易于理解的程序.20世纪60年代提出了结构化编程方法.
这种结构化的编程思想被像C这样的语言所应用,第一次使程序员可以相对轻松地编写适
度复杂的程序.然而,当一个工程项目达到一定规模后,即使使用结构化编程方法,编程
人员也无法对它的复杂性进行有效管理.20世纪80年代初期,许多工程项目的复杂性都超
过了结构化方法的极限.为解决这个问题,面向对象编程(object-oriented programming,
OOP)新方法诞生了.面向对象的编程在这本书的后面详细讨论,但在这里给出一个简短
的定义:面向对象的编程是通过使用继承性,封装性和多态性来帮助组织复杂程序的编程
方法.
总之,尽管C是世界上伟大的编程语言之一,但它处理复杂性的能力有限.一旦一个
程序的代码超过25 000~100 000行,就很难从总体上把握它的复杂性了.C++突破了这个限
制,帮助程序员理解并且管理更大的程序.
1979年,当Bjarne Stroustrup在新泽西州的Murray Hill实验室工作时,发明了C++.
Stroustrup 最初把这种新语言称为"带类的C".1983年,改名为C++.C++通过增加面向
对象的特性扩充了C.因为C++产生在C的基础之上,因此它包括了C所有的特征,属性和
优点.这是C++作为语言成功的一个关键原因.C++的发明不是企图创造一种全新的编程语
言,而是对一个已经高度成功的语言的改进.C++在1997年11月被标准化,目前的标准是
ANSI/ISO.
1.1.3 Java出现的时机已经到来
在20世纪80年代末和90年代初,使用面向对象编程的C++语言占主导地位.的确,有
一段时间程序员似乎都认为已经找到了一种完美的语言.因为C++有面向对象的特征,又
有C语言高效和格式上的优点,因此它是一种可以被广泛应用的编程语言.然而,就像过
去一样,推动计算机语言进化的力量正在酝酿.在随后的几年里,万维网(WWW)和Internet
达到临界状态.这个事件促成编程的另一场革命.
4 第1部分 Java语言
1.2 Java的产生
Java是由James Gosling,Patrick Naughton,Chris Warth,Ed Frank和Mike Sheridan于1991
年在Sun Microsystems公司设计出来的.开发第一个版本花了18个月.该语言开始名叫
"Oak",于1995年更名为"Java".从1992 的秋天Oak问世到1995的春天公开发布Java
语言,许多人对Java的设计和改进做出了贡献.Bill Joy,Arthur van Hoff,Jonathan Payne,
Frank Yellin和Tim Lindholm是主要的贡献者,正是他们的贡献使最初原型得以成熟.
说起来多少有些令人吃惊,Java的最初推动力并不是因特网!而是源于对独立于平台
(也就是体系结构中立)语言的需要,这种语言可创建能够嵌入微波炉,遥控器等各种家
用电器设备的软件.用作控制器的CPU芯片是多种多样的,但C和C++以及其他绝大多数语
言的缺点是只能对特定目标进行编译.尽管为任何类型的CPU芯片编译C++程序是可能的,
但这样做需要一个完整的以该CPU为目标的C++编译器,而创建编译器是一项既耗资巨大
又耗时较长的工作.因此需要一种简单且经济的解决方案.为了找到这样一种方案,Gosling
和其他人开始一起致力于开发一种可移植,跨平台的语言,该语言能够生成运行于不同环
境,不同CPU芯片上的代码.他们的努力最终促成了Java的诞生.
在Java的一些细节被设计出来的同时,第二个并且也是最重要的因素出现了,该因素
将对Java的未来起着至关重要的作用.这第二个因素当然就是万维网(WWW).如果万维
网(WWW)的成型和Java的实现不是同时发生的话,那么Java可能保持它有用,但默默无
闻的用于电子消费品编程语言的状态.然而,随着万维网的出现,Java被推到计算机语言
设计的最前沿,因为万维网也需要可移植的程序.
绝大多数程序员在涉足编程领域时就知道可移植的程序像他们的理想一样难以捉摸.
尽管人们对高效的,可移植的(独立于平台)编程方式的追寻几乎和编程历史一样久远,
但它总是让位于其他的更为紧迫的问题.此外,因为计算机业被Intel,Macintosh和UNIX
这3个竞争对手垄断,大多数程序员都在其中的某个领域内长期工作,所以对可移植语言的
需求就不是那么迫切.但是,随着因特网和Web的出现,关于可移植性语言的旧问题又被
提了出来.毕竟,因特网由不同的,分布式的系统组成,其中包括各种类型的计算机,操
作系统和CPU.尽管许多类型的平台都可以与因特网连接,但用户仍希望他们能够运行同
样的程序.曾经是一个令人烦恼却无需优先考虑的问题现在变成了急需解决的问题.
1993年,Java设计小组的成员发现他们在编制嵌入式控制器代码时经常遇到的可移植
性问题,在编制因特网代码的过程中也出现了.事实上,开始被设计为解决小范围问题的
Java语言同样可以被用在大范围的因特网上.这个认识使他们将Java的重心由电子消费品转
移到Internet编程.因此,中立体系结构编程语言的需要是促使Java诞生的源动力,而Internet
却最终导致了Java的成功.
正如前面提到的,Java的大部分特性是从C和C++中继承的.Java设计人员之所以故意
这么做,主要是因为他们觉得,在新语言中使用熟悉的C语法及模仿C++面向对象的特性,
将使他们的语言对经验丰富的C/C++程序员有更大的吸引力.除了表面类似外,其他一些
促使C和C++成功的因素也帮了Java的忙.首先,Java的设计,测试,精炼由真正从事编程
第1章 Java的起源 5
工作的人员完成,它根植于设计它的人员的需要和经验,因而也是一个程序员自己的语言.
其次,Java是紧密结合的且逻辑上是协调一致的.最后,除了那些Internet环境强加的约束
以外,Java给了编程人员完全的控制权.如果你程序编的好,你编写的程序就能反映出这
一点.相反,如果你的编程手法拙劣,也能在你的程序中反映出来.换一种说法,Java并
不是训练新手的语言,而是供专业编程人员使用的语言.
由于Java和C++之间的相似性,容易使人将Java简单地想象为"C++的版本".但其实
这是一种误解.Java在实践和理论上都与C++有重要的不同点.尽管Java受到C++的影响,
但它并不是C++的增强版.例如,Java与C++既不向上兼容,也不向下兼容.当然,Java与
C++的相似之处也是很多的,如果你是一个C++程序员,你会感觉到对Java非常熟悉.另外
一点是:Java并不是用来取代C++的,设计Java是为了解决某些特定的问题,而设计C++是
为了解决另外一类完全不同的问题.两者将长时间共存.
正如本章开始提到的,计算机语言的革新靠两个因素驱动:对计算环境改变的适应和
编程艺术的进步.环境的变化促使Java这种独立于平台的语言注定成为Internet上的分布式
编程语言.同时,Java也改变了人们的编程方式,特别是Java对C++使用的面向对象范例进
行的增强和完善.所以,Java不是孤立存在的一种语言,而是计算机语言多年来的演变结
果.仅这个事实就足以证明Java在计算机语言历史上的地位.Java对Internet编程的影响就
如同C对系统编程的影响一样:革命的力量将改变世界.
1.3 Java对Internet为什么重要
Internet使Java成为网上最流行的编程语言,同时Java对Internet的影响也意义深远.原
因相当简单:Java扩展了可以在赛百空间自由流动的对象的世界.在网络中,有两大类对
象在服务器和个人计算机之间传输:被动的信息和动态的,主动的程序.例如,当你阅读
电子邮件时,你在看被动的数据.甚至当你下载一个程序时,该程序的代码也是被动的数
据,直到你执行它为止.但是,可以传输到个人计算机的另一类对象却是:动态的,可自
运行的程序,虽然这类程序是客户机上的活动代理,但却是由服务器来初始化的.例如,
被服务器用来正确地显示服务器传送数据的程序.
网上程序在动态性上是令人满意的,但它们在安全性和可移植性方面也显示出严重的
缺陷.在Java产生以前,当前赛百空间有一半的对象实体无法进入网络世界,是Java为它们
打开了便利之门,而且在这个过程中定义了一种全新的程序形式:applet(小应用程序).
1.3.1 Java小应用程序和应用程序
Java可用来生成两类程序:应用程序(applications)和Java applet(小应用程序).应
用程序是可以在你的计算机的操作系统中运行的程序,从这一方面来说,用Java编制的应
用程序多多少少与使用C或C++编制的应用程序有些类似.在创建应用程序时,Java与其他
计算机语言没有大的区别.而Java的重要性就在于它具有编制小应用程序的功能.小应用
程序是可以在Internet中传输并在兼容Java的Web浏览器中运行的应用程序.小应用程序实
际上就是小型的Java程序,能像图像文件,声音文件和视频片段那样通过网络动态下载,
6 第1部分 Java语言
它与其他文件的重要差别是,小应用程序是一个智能的程序,能对用户的输入作出反应,
并且能动态变化,而不是一遍又一遍地播放同一动画或声音.
如果Java不能解决两个关于小应用程序的最棘手的问题:安全性和可移植性,那么小
应用程序就不会如此令人激动.在继续下一个话题之前,让我们先说明以下这两个术语对
Internet的意义.
1.3.2 安全性
正如你知道的那样,每次当你下载一个"正常"的程序时,你都要冒着被病毒感染的
危险.在Java出现以前,大多数用户并不经常下载可执行的程序文件;即使下载了程序,
在运行它们以前也都要进行病毒检查.尽管如此,大多数用户还是担心他们的系统可能被
病毒感染.除了病毒,另一种恶意的程序也必须警惕.这种恶意的程序可通过搜索你计算
机本地文件系统的内容来收集你的私人信息,例如信用卡号码,银行账户结算和口令.Java
在网络应用程序和你的计算机之间提供了一道防火墙(firewall),消除了用户的这些顾虑.
当使用一个兼容Java的Web浏览器时,你可以安全地下载Java小应用程序,不必担心病
毒的感染或恶意的企图.Java实现这种保护功能的方式是,将Java程序限制在Java运行环境
中,不允许它访问计算机的其他部分,后面将介绍这个过程是如何实现的.下载小应用程
序并能确保它对客户机的安全性不会造成危害是Java的一个最重要的方面.
1.3.3 可移植性
正如前面所讨论的,许多类型的计算机和操作系统都连接到Internet上.要使连接到
Internet上的各种各样的平台都能动态下载同一个程序,就需要有能够生成可移植性执行代
码的方法.很快你将会看到,有助于保证安全性的机制同样也有助于建立可移植性.实际
上,Java对这两个问题的解决方案是优美的也是高效的.
1.4 Java的魔力:字节码
Java解决上述两个问题——安全性和可移植性的关键在于Java编译器的输出并不是可
执行的代码,而是字节码(bytecode).字节码是一套设计用来在Java运行时系统下执行的
高度优化的指令集,该Java运行时系统称为Java虚拟机(JavaVirtual Machine,JVM).在其标
准形式下,JVM 就是一个字节码解释器.这可能有点让人吃惊,因为像C++之类语言的编
译结果是可执行的代码.事实上,出于对性能的考虑,许多现代语言都被设计为编译型,
而不是解释型.然而,正是通过JVM运行Java程序才有助于解决在Internet上下载程序的主
要问题.这就是Java输出字节码的原因.
将一个Java程序翻译成字节码,有助于它更容易地在一个大范围的环境下运行程序.
原因非常直接:只要在各种平台上都实现Java虚拟机就可以了.在一个给定的系统中,只
要系统运行包存在,任何Java程序就可以在该系统上运行.记住:尽管不同平台的Java虚拟
机的细节有所不同,但它们都解释同样的Java字节码.如果一个Java程序被编译为本机代码,
那么对于连接到Internet上的每一种CPU类型,都要有该程序的对应版本.这当然不是一个
第1章 Java的起源 7
可行的解决方案.因此,对字节码进行解释是编写真正可移植性程序的最容易的方法.
对Java程序进行解释也有助于它的安全性.因为每个Java程序的运行都在Java虚拟机的
控制之下,Java虚拟机可以包含这个程序并且能阻止它在系统之外产生副作用.正如你将
看到的,Java语言特有的某些限制增强了它的安全性.
被解释的程序的运行速度通常确实会比同一个程序被编译为可执行代码的运行速度慢
一些.但是对Java来说,这两者之间的差别不太大.使用字节码能够使Java运行时系统的程
序执行速度比你想象的快得多.
尽管Java被设计为解释执行的程序,但是在技术上Java并不妨碍动态将字节码编译为本
机代码.SUN公司在Java 2发行版中提供了一个字节码编译器——JIT(Just In Time,即时).
JIT是Java虚拟机的一部分,它根据需要,一部分一部分地将字节码实时编译为可执行代码.
它不能将整个Java程序一次性全部编译为可执行的代码,因为Java要执行各种检查,而这些
检查只有在运行时才执行.记住这一点是很重要的,因为JIT只编译它运行时需要的代码.
尽管如此,这种即时编译执行的方法仍然使性能得到较大提高.即使对字节码进行动态编
译后,Java程序的可移植性和安全性仍能得到保证,因为运行时系统(该系统执行编译)
仍然能够控制Java程序的运行环境.不管Java程序被按照传统方式解释为字节码,还是被动
态编译为可执行代码,其功能是相同的.
1.5 Java常用语
不介绍Java常用语,对Java的总体介绍就是不完整的.尽管促使Java诞生的源动力是可
移植性和安全性,但在Java语言最终成型的过程中,其他一些因素也起了重要的作用.Java
设计开发小组的成员总结了这些关键因素,称其为Java的专门用语,包括下面几个:
简单(Simple)
安全(Secure)
可移植(Portable)
面向对象(Object-oriented)
健壮(Robust)
多线程(Multithreaded)
体系结构中立(Architecture-neutral)
解释执行(Interpreted)
高性能(High performance)
分布式(Distributed)
动态(Dynamic)
在这些特性中,安全和可移植已经在前面介绍过了,下面让我们看看其他特性的含义.
1.5.1 简单
Java的设计目的是让专业程序员觉得既易学又好用.假设你有编程经历,你将不觉得
8 第1部分 Java语言
Java难掌握.如果你已经理解面向对象编程的基本概念,学习Java将更容易.如果你是一个
经验丰富的C++程序员,那就最好了,学习Java简直不费吹灰之力.因为Java继承C/C++语
法和许多C++面向对象的特性,大多数程序员在学习Java时都不会觉得太难.另外,C++中
许多容易混淆的概念,或者被Java弃之不用了,或者以一种更清楚,更易理解的方式实现.
除了和C/C++类似以外,Java的另外一个属性也使它更容易学习:设计人员努力使Java
中不出现显得让人吃惊的特性.在Java中,很少明确地告诉你如何才能完成一项特定的任
务.
1.5.2 面向对象
尽管受到其前辈的影响,但Java没被设计成兼容其他语言源代码的程序.这允许Java
开发组自由地从零开始.这样做的一个结果是,Java语言可以更直接,更易用,更实际的
接近对象. 通过对近几十年面向对象软件优点的借鉴,Java设法在纯进化论者的"任何事
物都是一个对象"和实用主义者的"不讨论对象不对象"的论点之间找到了平衡.Java的
对象模型既简单又容易扩展,对于简单数据类型,例如整数,它保持了高性能,但不是对
象.
1.5.3 健壮
万维网上多平台的环境使得它对程序有特别的要求,因为程序必须在许多系统上可靠
地执行.这样,在设计Java时,创建健壮的程序被放到了高度优先考虑的地位.为了获得
可靠性,Java在一些关键的地方限制你,强迫你在程序开发过程中及早发现错误.同时,
Java使你不必担心引起编程错误的许多最常见的问题.因为Java是一种严格的类型语言,它
不但在编译时检查代码,而且在运行时也检查代码.事实上,在运行时经常碰到的难以重
现的,难以跟踪的许多错误在Java中几乎是不可能产生的.要知道,使程序在不同的运行
环境中以可预见的方式运行是Java的关键特性.
为更好理解Java是如何具有健壮性的,让我们考虑使程序失败的两个主要原因:内存
管理错误和误操作引起的异常情况(也就是运行时错误).在传统的编程环境下,内存管理
是一项困难,乏味的任务.例如,在C/C++中,程序员必须手工地分配并且释放所有的动
态内存.这有时会导致问题,因为程序员可能忘记释放原来分配的内存,或者释放了其他
部分程序正在使用的内存.Java通过替你管理内存分配和释放,可以从根本上消除这些问
题(事实上,释放内存是完全自动的,因为Java为闲置的对象提供内存垃圾自动收集).
在传统的环境下,异常情况可能经常由"被零除"或"文件未找到"这样的情况引起,而
我们又必须用既繁多又难以理解的一大堆指令来对它们进行管理.Java通过提供面向对象
的异常处理机制来解决这个问题.一个写得很好的Java程序,所有的运行时错误都可以并
且应该被你的程序自己进行管理.
1.5.4 多线程
设计Java的目标之一是为了满足人们对创建交互式网上程序的需要.为此,Java支持多
线程编程,因而你用Java编写的应用程序可以同时执行多个任务.Java运行时系统在多线程
第1章 Java的起源 9
同步方面具有成熟的解决方案,这使你能够创建出运行平稳的交互式系统.Java的多线程
机制非常好用,因而你只需关注程序细节的实现,不用担心后台的多任务系统.
1.5.5 结构中立
Java设计者考虑的一个主要问题是程序代码的持久性和可移植性.程序员面临的一个
主要问题是,不能保证今天编写的程序明天能否在同一台机器上顺利运行.操作系统升级,
处理器升级以及核心系统资源的变化,都可能导致程序无法继续运行.Java设计者对这个
问题做过多种尝试,Java虚拟机(JVM)就是试图解决这个问题的.他们的目标是"只要
写一次程序,在任何地方,任何时间该程序永远都能运行".在很大程度上,Java实现了
这个目标.
1.5.6 解释性和高性能
前面已提到,通过把程序编译为Java字节码这样一个中间过程,Java可以产生跨平台运
行的程序.字节码可以在提供Java虚拟机(JVM)的任何一种系统上被解释执行.早先的
许多尝试解决跨平台的方案对性能要求都很高.其他解释执行的语言系统,如BASIC,Tcl,
PERL都有无法克服的性能缺陷.然而,Java却可以在非常低档的CPU上顺利运行.前面已
解释过,Java确实是一种解释性语言,Java的字节码经过仔细设计,因而很容易便能使用JIT
编译技术将字节码直接转换成高性能的本机代码.Java运行时系统在提供这个特性的同时
仍具有平台独立性,因而"高效且跨平台"对Java来说不再矛盾.
1.5.7 分布式
Java为Internet的分布式环境而设计,因为它处理TCP/IP协议.事实上,通过URL地址
存取资源与直接存取一个文件的差别是不太大的.Java原来的版本(Oak) 包括了内置的地址
空格消息传递(intra-address-space)特性.这允许位于两台不同的计算机上的对象可以远程地
执行过程.Java最近发布了叫做远程方法调用(Remote Method Invocation ,RMI)的软件
包,这个特性使客户机/服务器编程达到了无与伦比的抽象级.
1.5.8 动态
Java程序带有多种的运行时类型信息,用于在运行时校验和解决对象访问问题.这使
得在一种安全,有效的方式下动态地连接代码成为可能,对小应用程序环境的健壮性也十
分重要,因为在运行时系统中,字节码内的小段程序可以动态地被更新.
1.6 继 续 革 命
Java的最初发布本不亚于一场革命,但是它并不标志着Java快速革新时代的结束.与大
多数其他软件系统经常进行小的改进不同,Java继续以爆炸式的步伐向前发展.在Java 1.0
发布不久,Java的设计者已经创造出了Java 1.1.Java 1.1新增的特性远比普通意义上的版本
修订有意义,内容要丰富许多.Java 1.1增加了许多新的库元素,重新定义了小应用程序处
10 第1部分 Java语言
理事件的方法,并且重新设置了1.0版中库的许多特性.它也放弃了原来由Java1.0定义的若
干过时的特征.因此,Java 1.1不但增加了Java 1.0中没有的属性,同时也抛弃了一些原有的
属性.
Java的第二个主要发布版本是Java 2.Java 2 是一个分水岭,它标志这个快速演变语言
"现代时代"的开始!Java 2第一版本的版本号是1.2.这似乎有点奇怪.原因是它参考了
原来Java库的版本,对于整个版本来说,它本身没有多大变化.Java 2增加了很多对新特性
的支持,例如Swing和类集框架,并且它提高了Java虚拟机和各种编程工具的性能.Java 2
也包含了一些不赞成继续使用的内容,主要是不赞成使用线程类中suspend( ),resume( )和
stop( )这些方法.
Java的当前版本是Java 2,1.3版.Java的这个版本是对Java 2原来版本的第一次最主要
的升级.该版本增强了Java大部分现有的功能,并且限制了它的开发环境.总的来说,版
本1.2和版本1.3的程序源代码是兼容的.尽管与前面3个版本相比,版本1.3作了一些小的改
变,但这是无关紧要的.
本书适合Java 2的1.2和1.3版.当然,大多数内容也适用于Java早期的版本.在本书中,
当一个特性只适用于Java的一个特定的版本时,会被注明.否则,你就可以认为它适用于
一般的Java版本.另外,对于适用于Java 2两个版本的那些特性,本书中将简单地使用术语
Java 2,而不注明版本号.
1.7 Java不是增强的HTML
在继续讲解前,有必要澄清一个普遍的误解.因为Java被用来创建网页,所以初学者
有时将Java与超文本标记语言(HTML)混淆,或认为Java仅仅是对HTML的一些改进.幸
好,这只是误解.实质上,HTML是一种定义信息逻辑的组织方式并提供相关信息的链接
(叫超文本链接).你可能知道,超文本链接(hypertext link)(也叫超链接)是把一个超
文本与另一个超文本文档链接起来的工具,而这个被链接的超文本文档可能在本地或万维
网上其他地方.超文本文档要素的定义是通过选择该超文本文档与另一个相关文档的链接,
在用户搜索各种路径后,该超文本文档可以非线性的方式阅读.
尽管HTML允许用户以动态方式阅读文档,但HTML永远无法成为一种编程语言.当
然,HTML确实帮助和推进了万维网的普及,HTML是促使Java诞生的催化剂,但它没有直
接从概念上影响Java语言的设计.HTML与Java的惟一联系是,HTML提供Java小应用程序
标记,该标记启动Java小应用程序.这样,就可以在超文本文档中嵌入能启动Java小应用程
序的指令.
第2章 Java语言概述
像所有其他的计算机语言一样,Java的各种要素不是独立存在的,它们作为一个整体
共同构成了Java语言.这种关联使得不讲其他方面而单独描述Java的某一方面是困难的.讨
论一个特性经常要先具有另外一个特性的知识.因此,本章先对Java的若干主要特性做简
单综述.这里描述的主题将给你一个立足点:能够使你编写和理解简单的Java程序.大多
数讨论话题将在第1部分的其他章节详细叙述
2.1 面向对象编程
Java的核心是面向对象编程.事实上,所有的Java程序都是面向对象的,你别无选择.
这一点与C++不同,因为在那里你可以选择是否面向对象编程.面向对象编程与Java密不可
分,因此,在你编写哪怕是最简单的Java程序以前,也必须理解它的基本原则.因此,本
章先从面向对象编程的概念讲起.
2.1.1 两种范型
我们知道,所有的计算机程序都由两类元素组成:代码和数据.此外,从概念上讲,
程序还可以以它的代码或是数据为核心进行组织编写.也就是说,一些程序围绕"正在发
生什么"编写,而另一些程序则围绕"谁将被影响"编写.这两种范型决定程序的构建方
法.第一种方法被称为面向过程的模型(process-oriented model),用它编写的程序都具有
线性执行的特点.面向过程的模型可认为是代码作用于数据,像C这样的过程式语言采用
这个模型是相当成功的.然而,正如在第1章提到的,当程序变得更大并且更复杂时,就会
出现问题.
为了管理不断增加的复杂性,第二种方式,也就是面向对象的编程(object-oriented
programming)被构思出来了.面向对象的编程围绕它的数据(即对象)和为这个数据严格
定义的接口来组织程序.面向对象的程序实际上是用数据控制对代码的访问.下面你将看
到,将控制的实体变换为数据,可使程序在组织结构上从若干方面受益.
2.1.2 抽象
面向对象编程的一个实质性的要素是抽象.人们通过抽象(abstraction)处理复杂性.
例如,人们不会把一辆汽车想象成由几万个互相独立的部分所组成的一套装置,而是把汽
车想成一个具有自己独特行为的对象.这种抽象使人们可以很容易地将一辆汽车开到杂货
店,而不会因组成汽车各部分零件过于复杂而不知所措.他们可以忽略引擎,传动及刹车
系统的工作细节,将汽车作为一个整体来加以利用.
使用层级分类是管理抽象的一个有效方法.它允许你根据物理意义将复杂的系统分解
12 第1部分 Java语言
为更多更易处理的小块.从外表看,汽车是一个独立的对象.一旦到了内部,你会看到汽
车由若干子系统组成:驾驶系统,制动系统,音响系统,安全带,供暖,便携电话,等等.
再进一步细分,这些子系统由更多的专用元件组成.例如,音响系统由一台收音机,一个
CD播放器,或许还有一台磁带放音机组成.从这里得到的重要启发是,你通过层级抽象对
复杂的汽车(或任何另外复杂的系统)进行管理.
复杂系统的分层抽象也能被用于计算机程序设计.传统的面向过程程序的数据经过抽
象可用若干个组成对象表示,程序中的过程步骤可看成是在这些对象之间进行消息收集.
这样,每一个对象都有它自己的独特行为特征.你可以把这些对象当作具体的实体,让它
们对告诉它们做什么事的消息作出反应.这是面向对象编程的本质.
面向对象的概念是Java 的核心,对程序员来讲,重要的是要理解这些概念怎么转化为
程序.你将会发现,在任何主要的软件工程项目中,软件都不可避免地要经历概念提出,
成长,衰老这样一个生命周期,而面向对象的程序设计,可以使软件在生命周期的每一个
阶段都处变不惊,有足够的应变能力.例如,一旦你定义好了对象和指向这些对象的简明
的,可靠的接口,你就能很从容很自信地解除或更替旧系统的某些组成部分.
2.1.3 面向对象编程的3个原则
所有面向对象的编程语言都提供帮助你实现面向对象模型的机制,这些机制是封装,
继承及多态性.现在让我们来看一下它们的概念.
封装
封装(Encapsulation)是将代码及其处理的数据绑定在一起的一种编程机制,该机制
保证了程序和数据都不受外部干扰且不被误用.理解封装性的一个方法就是把它想成一个
黑匣子,它可以阻止在外部定义的代码随意访问内部代码和数据.对黑匣子内代码和数据
的访问通过一个适当定义的接口严格控制.如果想与现实生活中的某个事物作对比,可考
虑汽车上的自动传送.自动传送中包含了有关引擎的数百比特的信息,例如你正在以什么
样的加速度前进,你行驶路面的坡度如何,以及目前的档位.作为用户,你影响这个复杂
封装的方法仅有一个:移动档位传动杆.例如,你不能通过使用拐弯信号或挡风玻璃擦拭
器影响传动.所以档位传动杆是把你和传动连接起来的惟一接口.此外,传动对象内的任
何操作都不会影响到外部对象,例如,档位传动装置不会打开车前灯!因为自动传动被封
装起来了,所以任何一家汽车制造商都可以选择一种适合自己的方式来实现它.然而,从
司机的观点来看,它们的用途都是一样的.与此相同的观点能被用于编程.封装代码的好
处是每个人都知道怎么访问它,但却不必考虑它的内部实现细节,也不必害怕使用不当会
带来负面影响.
Java封装的基本单元是类.尽管类将在以后章节详细介绍.现在仍有必要对它作一下
简单的讨论.一个类(class)定义了将被一个对象集共享的结构和行为(数据和代码).
一个给定类的每个对象都包含这个类定义的行为和结构,好像它们是从同一个类的模子中
铸造出来似的.因为这个原因,对象有时被看作是类的实例(instances of a class).所以,
类是一种逻辑结构,而对象是真正存在的物理实体.
当创建一个类时,你要指定组成那个类的代码和数据.从总体上讲,这些元素都被称
第2章 Java语言概述 13
为该类的成员(members).具体地说,类定义的数据称为成员变量(member variables)
或实例变量(instance variables).操作数据的代码称为成员方法(member methods)或简
称方法(methods).如果你对C/C++熟悉,可以这样理解:Java程序员所称的方法,就是
C/C++程序员所称的函数(function).在完全用Java编写的程序中,方法定义如何使用成
员变量.这意味着一个类的行为和接口是通过方法来定义的,类这些方法对它的实例数据
进行操作.
既然类的目的是封装复杂性,在类的内部就应该有隐藏实现复杂性机制.类中的每个
方法或变量都可以被标记为私有(private)或公共(public).类的公共接口代表类的外部
用户需要知道或可以知道的每件事情;私有方法和数据仅能被一个类的成员代码所访问,
其他任何不是类的成员的代码都不能访问私有的方法或变量.既然类的私有成员仅能被程
序中的其他部分通过该类的公共方法访问,那么你就能保证不希望发生的事情就一定不会
发生.当然,公共接口应该小心仔细设计,不要过多暴露类的内部内容(见图2-1).
图2-1 封装:可用来保护私有数据的公共方法
继承
继承(Inheritance)是一个对象获得另一个对象的属性的过程.继承很重要,因为它支
持了按层分类的概念.如前面提到的,大多数知识都可以按层级(即从上到下)分类管理.
例如,尊贵的猎犬是狗类的一部分,狗又是哺乳动物类的一部分,哺乳动物类又是动物类
的一部分.如果不使用层级的概念,我们就不得不分别定义每个动物的所有属性.使用了
继承,一个对象就只需定义使它在所属类中独一无二的属性即可,因为它可以从它的父类
那儿继承所有的通用属性.所以,可以这样说,正是继承机制使一个对象成为一个更具通
用类的一个特定实例成为可能.下面让我们更具体地讨论这个过程.
大多数人都认为世界是由对象组成的,而对象又是按动物,哺乳动物和狗这样的层级
结构相互联系的.如果你想以一个抽象的方式描述动物,那么你可以通过大小,智力及骨
14 第1部分 Java语言
胳系统的类型等属性进行描述.动物也具有确定的行为,它们也需要进食,呼吸,并且睡
觉.这种对属性和行为的描述就是对动物类的定义.
如果你想描述一个更具体的动物类,比如哺乳动物,它们会有更具体的属性,比如牙
齿类型,乳腺类型等.我们说哺乳类动物是动物的子类(subclass),而动物是哺乳动物的
超类(superclass).
由于哺乳动物类是需要更加精确定义的动物,所以它可以从动物类继承(inherit)所有
的属性.一个深度继承的子类继承了类层级(class hierarchy)中它的每个祖先的所有属性.
继承性与封装性相互作用.如果一个给定的类封装了一些属性,那么它的任何子类将
具有同样的属性,而且还添加了子类自己特有的属性(见图2-2).这是面向对象的程序在
复杂性上呈线性而非几何性增长的一个关键概念.新的子类继承它的所有祖先的所有属性.
它不与系统中其余的多数代码产生无法预料的相互作用.
多态性
多态性(Polymorphism,来自于希腊语,表示"多种形态")是允许一个接口被多个
同类动作使用的特性,具体使用哪个动作与应用场合有关,下面我们以一个后进先出型堆
栈为例进行说明.假设你有一个程序,需要3种不同类型的堆栈.一个堆栈用于整数值,一
个用于浮点数值,一个用于字符.尽管堆栈中存储的数据类型不同,但实现每个栈的算法
是一样的.如果用一种非面向对象的语言,你就要创建3个不同的堆栈程序,每个程序一个
名字.但是,如果使用Java,由于它具有多态性,你就可以创建一个通用的堆栈程序集,
它们共享相同的名称.
多态性的概念经常被说成是"一个接口,多种方法".这意味着可以为一组相关的动
作设计一个通用的接口.多态性允许同一个接口被必于同一类的多个动作使用,这样就降
低了程序的复杂性.选择应用于每一种情形的特定的动作(specific action)(即方法)是
编译器的任务,程序员无需手工进行选择.你只需记住并且使用通用接口即可.
第2章 Java语言概述 15
图2-2 拉不拉多猎犬继承所有其超类的封装
再拿狗作比喻,一条狗的嗅觉是多态的.如果狗闻到猫的气味,它会在吠叫并且追着
它跑.如果狗闻到食物的气味,它将分泌唾液并向盛着食物的碗跑去.两种状况下是同一
种嗅觉器官在工作,差别在于闻到了什么气味,也就是有两种不同类型的数据作用于狗的
鼻子!在一个Java程序中使用方法时,也可以采用这个通用的概念.
多态性,封装性与继承性相互作用
如果用得当,在由多态性,封装性和继承性共同组成的编程环境中可以写出比面向过
程模型环境更健壮,扩展性更好的程序.精心设计的类层级结构是重用你花时间和努力改
进并测试过的程序的基础,封装可以使你在不破坏依赖于类公共接口的代码基础上对程序
进行升级迁移,多态性则有助于你编写清楚,易懂,易读,易修改的程序.
在前面两个与现实生活有关的实例中,汽车更能全面说明面向对象设计的优点,为介
绍继承而用狗作类比也很有趣.总的来说,汽车与程序很相似,所有的驾驶员依靠继承性
很快便能掌握驾驶不同类型(子类)车辆的技术.不管是接送学生的校车,或是默西迪斯
16 第1部分 Java语言
私家轿车,或是保时捷汽车,或是家庭汽车,司机差不多都能找到方向盘,制动闸和加速
器,并知道如何操作.经过一段驾驶,大多数人甚至能知道手动档与自动档之间的差别,
因为他们从根本上理解这两个档的超类——传动.
人们在汽车上看见的总是封装好的特性.刹车和踏脚板隐蔽着不可思议的复杂性,但
接口却是如此简单,你的脚就可以操作它们!引擎,制动闸及轮胎的大小对于你如何定义
踏脚板类的接口没有任何影响.
最后的属性,多态性,在汽车制造商基于相同的交通工具所提供的多种选择的能力上
得到了充分反映.例如,刹车系统有正锁和反锁之分,方向盘有带助力或不带助力之分,
引擎有4缸,6缸或8缸之分.无论设置如何,你都得脚踩刹车板来停车,转动方向盘来转向,
按离合器来制动.同样的接口能被用来控制许多不同的实现过程.
正如你所看到的,通过封装,继承及多态性原理,各个独立部分组成了汽车这个对象.
这在计算机程序设计中也是一样的.通过面向对象原则的使用,可以把程序的各个复杂部
分组合成一个一致的,健壮的,可维护的程序整体.
正如本节开始时提到的,所有的Java程序都是面向对象的.或者,更精确地说,每个
Java程序都具有封装性,继承性及多态性.尽管在本章将要介绍的简单示例程序及以后几
章的示例程序中并未体现所有这些特性,但也有所体现.你将看到,Java提供的许多特性
是它的内置类库的一部分,这个库使封装性,继承性及多态性得到更广泛应用.
2.2 第1个简单程序
既然Java面向对象的基础已经被讨论过了,接下来让我们看一些实际的Java程序.让我
们从编译及运行下面这个简短示例程序开始.你将看到,这个程序的功能比你想像的要多.
/*
This is a simple Java program.
Call this file "Example.java".
*/
class Example {
// Your program begins with a call to main().
public static void main(String args[]) {
System.out.println("This is a simple Java program.");
}
}
注意:在下面的介绍中,将使用标准JDK(Java Developer's Kit,Java 开发工具
包),它可从Sun Microsystems公司得到.如果你正在使用其他的Java开发环境,
则Java程序编译,运行过程可能有所不同.在这种情况下,请你查阅编译器的用户
手册来获得具体指导.
2.2.1 键入程序
对大多数计算机语言,包含程序源代码的文件名是任意的,但对于Java就不行.关于
Java,你需要知道的第一件事就是源文件的名字非常重要.对这个例子,源程序文件名应
第2章 Java语言概述 17
该是Example.java.下面我们将解释其中的原因.
在Java中,一个源程序文件被称为一个编译单元(compilation unit).它是一个包含一
个或多个类定义的文本文件.Java编译器要求源程序文件使用.java文件扩展名.请注意,
文件扩展名长度是4个字符.所以,你的操作系统一定要有支持长文件名的能力.这意味着
DOS和Windows 3.1是不支持Java的(至少在目前是这样).当然,它可在Windows 95/98和
Windows NT/2000下正常工作.
从上述示例程序中可以看出,程序中定义的类名也是Example.这不是巧合.在Java中,
所有的代码都必须驻留在类中.按照约定,类名必须与源程序的文件名相同.你也应该确
保文件名的大小写字母与类名一样,这是因为Java是区分大小写的.虽然文件名与类名必
须一致的约定似乎有点专制,但是这个约定有助于你轻松地维护及组织程序.
2.2.2 编译程序
要编译示例程序Example,须运行编译器程序javac,并在命令行上指定源程序文件名,
格式如下所示:
C:\>javac Example.java
编译器javac产生了一个名为Example.class的文件,该文件包含程序的字节码.前面已
讨论过,Java字节码中包含的是Java解释程序将要执行的指令码.因此,javac的输出并不
是可以直接运行的代码.
要真正运行该程序,你必须使用名叫java的Java解释器.具体做法是把类名Example作
为一个命令行参数输入,格式如下所示:
C:\>java Example
运行这个程序,将输出如下内容:
This is a simple Java program.
当Java源代码被编译后,每个单独的类都被放入自己的输出文件中,并以类的名字加
".class"扩展名为其文件名.这就是为什么Java源程序文件必须与其中包含的类同名的原
因——源程序文件将与".class"文件相同.运行Java解释器实际上是指定你想要解释器运
行的类的名字,它会自动搜索包含该名字且带有.class扩展名的文件.如果找到,它将运行
包含在该指定类中的代码.
2.2.3 详细讨论第1个示例程序
尽管Example.java很短,但它包括了所有Java程序具有的几个关键特性.让我们仔细分
析该程序的每个部分.
程序开始于以下几行:
/*
This is a simple Java program.
Call this file "Example.java".
*/
18 第1部分 Java语言
这是一段注释(comment).像大多数其他的编程语言一样,Java也允许你在源程序文
件中加注释.注释中的内容将被编译器忽略.事实上,注释是为了给任何阅读源代码程序
的人说明或解释程序的操作.在本例中,注释对程序进行说明,并提醒你该源程序的名字
叫做Example.java.当然,在真正的应用中,注释通常用来解释程序的某些部分如何工作或
某部分的特殊功能.
Java支持3种类型的注释.在示例程序顶部的注释称为多行注释(multiline comment).
这类注释开始于"/*",结束于"*/".这两个注释符间的任何内容都将被编译器忽略.正
如"多行注释"名字所示,一个多行注释可以包含若干行文字.
程序的下一行代码如下所示:
class Example {
该行使用关键字class声明了一个新类,Example是类名标识符,整个类定义(包括其所
有成员)都将位于一对花括号({})之间,花括号在Java中的使用方式与C或C++相同,目
前,不必考虑类的细节,只是有一点要注意,在Java中,所有程序活动都发生在类内,这
就是为什么Java程序是面向对象的.
下面一行程序是单行注释:
// Your program begins with a call to main().
这是Java支持的第二种类型的注释.单行注释(single-line comment)始于"//",在该
行的末尾结束.通常情况下,程序员们对于较长的注释使用多行注释,而对于简短的,一
行一行的注释则使用单行注释.
下一行代码如下所示:
public static void main(String args[]) {
该行开始于main( )方法.正如它前面的注释所说,这是程序将要开始执行的第一行.
所有的Java应用程序都通过调用main( )开始执行(这一点同C/C++一样),我们在此还不能
对该行的每一个部分作出精确的解释,因为这需要详细了解Java封装性的特点,但是,由
于本书第1部分中的大多数例子都用到这一行代码,我们将对各部分作一个简单介绍.
关键字public是一个访问说明符(access specifier),它允许程序员控制类成员的可见
性.如果一个类成员前面有public,则说明该成员能够被声明它的类之外的代码访问(与
public相对的是private,它禁止成员被所属类之外的代码访问).在本例中,main( )必须被
定义为public类型,因为当程序开始执行时它需要被它的类之外的代码调用.关键字static
允许调用main( )而不必先实现该类的一个特殊实例.这是必要的,因为在任何对象被创建
之前,Java解释器都会调用main().关键字void仅通知编译器main()不返回任何值.你将看
到,方法也可以有返回值.如果这一切似乎有一点令人费解,别担心.所有这些概念都将
在随后的章节中详细讨论.
前面已经介绍过,main()是Java程序开始时调用的方法.请记住,Java是区分大小写的.
因此,main与Main是不同的.Java编译器也可以编译不包含main()方法的类,但是Java解释
程序没有办法运行这些类.因此,如果你输入了Main而不是main,编译器仍将编译你的程
序,但Java解释程序将报告一个错误,因为它找不到main()方法.
第2章 Java语言概述 19
你要传递给方法的所有信息由方法名后面括号中指定的变量接收,这些变量被称为参
数(parameters).即使一个方法不需要参数,你仍然需要在方法名后面放置一对空括号.
在main()中,只有一个参数,即String args[],它声明了一个叫做args的参数,该参数是String
类的一个实例数组(注:数组是简单对象的集合).字符串类型的对象存储字符的串.在
本例中,args接收程序运行时显示的任何命令行参数.本例中的这个程序并没有使用这些
信息,但是本书后面讲到的其他一些程序将使用它们.
该行的最后一个字符是"{".它表示了main()程序体的开始.一个方法中包含的所有
代码都将包括在这对花括号中间.
另外,main( )仅是解释器开始工作的地方.一个复杂的程序可能包含几十个类,但这
些类仅需要一个main( )方法以供解释器开始工作.当你开始引用被嵌入在浏览器中的Java
小应用程序时,你根本不用使用main( )方法,因为Web浏览器使用另一种不同的方法启动
小应用程序.
接下来的代码行如下所示.请注意,它出现在main( )内.
System.out.println("This is a simple Java program.");
本行在屏幕上输出字符串"This is a simple Java program.",输出结果后面带一个空行.
输出实际上是由内置方法 println ( )来实现的.在本例中,println ( )显示传递给它的字符串.
你将会看到,println ( )方法也能用来显示其他类型的信息.该行代码开始于System.out,现
在对它作详细说明为时尚早,需涉及很多复杂内容.简单的说,System是一个预定义的可
访问系统的类,out是连接到控制台的输出流.
可能你已经猜到了,控制台输出(输入)在实际的Java程序和小应用程序中并不经常
使用.因为绝大多数现代计算环境从本质上讲都是窗口和图形界面的,控制台I/O主要被用
简单的实用工具程序和演示程序使用.在本书后面,你将会学到用Java生成输出的其他方
法.但是目前,我们将继续使用控制台I/O方法.
请注意,println( )语句以一个分号结束.在Java中,所有的语句都以一个分号结束.该
程序的其他行没有以分号结束,这是因为从技术上讲,它们并不是程序语句.
程序中的第一个"}"号结束了main( ),而最后一个"}"号结束类Example的定义.
2.3 第2个示例程序
对于编程语言来说,变量是一个最为基本的概念.你可能知道,变量是一个有名字的
内存位置,它能够被赋值.而且,在程序的运行过程中,变量的值是可以改变的.下一个
程序将介绍如何声明变量,如何给变量赋值.另外,该程序也说明了控制台输出的某些新
特点.从程序开始的注释可以看出,你应该把这个文件命名为Example2.java.
/*
Here is another short example.
Call this file "Example2.java".
*/
class Example2 {
public static void main(String args[]) {
20 第1部分 Java语言
int num; // this declares a variable called num
num = 100; // this assigns num the value 100
System.out.println("This is num: " + num);
num = num * 2;
System.out.print("The value of num * 2 is ");
System.out.println(num);
}
}
运行该程序时,你将会看到如下的运行结果:
This is num: 100
The value of num * 2 is 200
让我们来进一步查看这个结果是如何产生的.我们重点考虑与前一示例不同的代码,
在上一个程序中未出现的第一行代码是:
int num; // this declares a variable called num
该行声明了一个名为num的整型变量.和其他大多数语言一样,在Java中一定要先声明
变量,然后再使用变量.
下面是声明变量的一般形式:
type var-name;
在这里,type表示所要声明的变量的类型,var-name是所要声明变量的名称.如果你要
声明多个属于同一类型的变量,只需用逗号将各个变量名分开即可.Java定义了几种数据
类型:整型(integer),字符型(character),浮点型(floating-point).关键字int指的是
整数类型.
在程序中,下面这一行代码将100赋予变量num.
num = 100; // this assigns num the value 100
在Java中,赋值符号是等号.
下面的这行程序在输出变量值之前,先输出字符串"This is num:".
System.out.println("This is num: " + num);
在这个语句中,变量num之前的加号"+"的作用是,让num的取值与它前面的字符串
相连接,然后再输出结果字符串的内容(实际上,变量num先被它赋值再超值转换成字符
串,然后再和加号之前的字符串相连接.这个过程将在本书的后面详细讨论).这种方法
可以被推广.通过加号"+"的连接操作,你可以在println()这个方法之内将尽可能多的
字符串内容连在一起.
接下来的语句行将变量num乘2以后的结果重新赋值给变量num.和其他大多数语言一
样,Java用"*"符号来表示乘法运算.在执行这行语句之后,变量num的值变成了200.
本程序接下来的两行代码是:
第2章 Java语言概述 21
System.out.print("The value of num * 2 is ");
System.out.println(num);
在这两行中有几个新内容.首先,内置方法print( )被用来显示字符串"The value of num
* 2 is".该字符串后面不换行,这意味着如果生成第二个输出,它将在同一行中开始输出.
方法print ( )和方法println ( )类似,只是它在每次调用后并不输出一个新行(即换行).其
次,在调用println ( )时,注意变量num可以被自身使用.方法print ( )和方法println ( )都能够
用来输出Java的任何内置类型的值.
2.4 两个控制语句
尽管将在第5章仔细讨论控制语句,我们还是在这里先简单介绍2条控制语句,以便能
在第3章,第4章中的例子程序中使用它们,并且它们也将帮助说明Java的一个重要特点:
程序块.
2.4.1 if控制语句
Java中if控制语句与其他语言中的IF语句非常相似.并且,它与C/ C++语言中的if语句
的语法完全相同.它的最简单形式如下:
if(condition) statement;
这里,条件condition是一个布尔型表达式.如果条件为真,那么执行语句statement;如
果条件为假,则语句statement将被绕过而不被执行.下面是一个例子:
if(num < 100) println("num is less than 100");
在这个例子中,如果变量num的值小于100,那么条件表达式的值为真,方法 println ( )
将被调用执行.如果变量num的值大于或等于100,那么方法println ( )被绕过而不被执行 .
在第4章,中你将看到Java在条件语句中用到的所有的关系运算符,下面是其中一部分:
运算符 含 义
大于
== 等于
注意,判断是否相等的关系运算符是两个等号"==".
下面的程序说明了if控制语句的用法:
/*
Demonstrate the if.
Call this file "IfSample.java".
*/
class IfSample {
public static void main(String args[]) {
22 第1部分 Java语言
int x,y;
x = 10;
y = 20;
if(x y) System.out.println("x now greater than y");
// this won't display anything
if(x == y) System.out.println("you won't see this");
}
}
该程序产生的结果如下所示:
x is less than y
x now equal to y
x now greater than y
这个程序中另一个需要注意的地方是:
int x ,y ;
该程序行使用逗号来分隔变量列表,定义了2个变量x和y.
2.4.2 for循环
你可能从先前的编程经验已经知道,在几乎所有的编程语言中,循环语句都是其重要
组成部分.Java也不例外.事实上,你将在第5章中看到,Java提供了一套功能强大的循环
结构.For循环也许是最通用的.如果你对C或C++熟悉,那么你应该感到高兴,因为Java
的for循环和其他语言中的for循环操作完全一样.如果你不熟悉C/C++,for循环也是容易使
用的.最简单的for循环结构如下所示:
for(initialization; condition; iteration) statement;
在这个最常见的形式中,循环体的初始化部分(initialization)设置循环变量并为变量
赋初始值.条件判断部分(condition)是测试循环控制变量的布尔表达式.如果测试的结
果为真,循环体(statement)继续反复执行;如果测试的结果为假,循环结束.迭代部分
(iteration)的表达式决定循环控制变量在每次循环后是如何改变的.下面的短程序说明了
for循环的使用方法:
/*
Demonstrate the for loop.
Call this file "ForTest.java".
*/
class ForTest {
public static void main(String args[]) {
第2章 Java语言概述 23
int x;
for(x = 0; x<10; x = x+1)
System.out.println("This is x: " + x);
}
}
这个程序产生的结果如下:
This is x: 0
This is x: 1
This is x: 2
This is x: 3
This is x: 4
This is x: 5
This is x: 6
This is x: 7
This is x: 8
This is x: 9
在这个例子中,x是循环控制变量.它在for的初始化部分被初始化为零.在每次重复
迭代(包括第一次)的开始,执行条件测试x< 10.如果测试的结果为真,println ( )语句被
执行,然后执行循环体的迭代部分.这个过程将持续进行下去,直到条件测试的结果为假.
有趣的是,在Java专业程序员编写的程序中,循环体的迭代部分一般不会像前面程序
示例那样.即你很少会看到下面的语句:
x = x + 1;
原因是Java有一个特殊的增量运算符,能够更高效地执行这项操作.该增量运算符是
"++"(即2个加号).递增运算符每次使其作用对象加1.通过使用递增运算符,上条语句
可以这样写:
x++;
这样,前述的for循环语句通常写成这样:
for(x = 0; x<10; x++)
你可以将上一个程序的for循环语句改写成这样试一下.你将看到,运行结构与以前相
同.
Java也提供一个递减运算符:"--"(即2个减号).递减运算符使其作用对象每次减1.
2.5 使用程序块
在Java中,可以将2个或2个以上的语句组成一组,这样的一组语句称为程序块
(Codeblocks).程序块是通过将所属语句放在花括号中来实现.一旦创建了程序块,
它就成为一个逻辑单元,可以作为一个单独的语句来使用.例如,程序块可以作为Java
中if控制语句和for控制语句的目标.我们来看一看下面的if控制语句:
24 第1部分 Java语言
if(x < y) { // begin a block
x = y;
y = 0;
} // end of block
本例中,如果x小于y,那么在程序块内的两条语句都将执行.因此,程序块中的这2
条语句组成一个逻辑单元,不能一条语句运行,而另一条语句不运行.其中的关键一点是
如果你需要将两个或多个语句在逻辑上连接起来 ,你就可以将其放入一个程序块中.
让我们看另外的例子.下面的程序将for循环作为一个程序块使用.
/*
Demonstrate a block of code.
Call this file "BlockTest.java"
*/
class BlockTest {
public static void main(String args[]) {
int x,y;
y = 20;
// the target of this loop is a block
for(x = 0; x<10; x++) {
System.out.println("This is x: " + x);
System.out.println("This is y: " + y);
y = y - 2;
}
}
}
这个程序产生的结果如下所示:
This is x: 0
This is y: 20
This is x: 1
This is y: 18
This is x: 2
This is y: 16
This is x: 3
This is y: 14
This is x: 4
This is y: 12
This is x: 5
This is y: 10
This is x: 6
This is y: 8
This is x: 7
This is y: 6
This is x: 8
This is y: 4
This is x: 9
This is y: 2
在本例中,for循环作为一个程序块使用,而并不是一个单独的语句.这样,每循环一
第2章 Java语言概述 25
次,块内的3条语句都要运行一次.这个事实当然被程序的执行结果证实了.
在本书的后面,你会看到程序块的其他性质和用法.当然,它们存在的主要原因是为
了创建逻辑上独立的代码单元.
2.6 基 本 词 汇
既然你已经看过了几个短的Java程序,现在让我们更正式的介绍Java的基本元素.Java
程序由空白分隔符,标识符,注释,文字,运算符,分隔符,以及关键字组成.运算符将
在下一章详细讨论,本节讨论其他的元素.
2.6.1 空白分隔符(whitespace)
Java 是一种形式自由的语言.这意味着你不需要遵循任何特殊的缩进书写规范.例如,
例子程序的所有代码都可以在一行上,你也可以按自己喜欢的方式输入程序代码,前提是
必须在已经被运算符或分隔符描述的标记之间至少留出一个空白分隔符.在Java中,空白
分隔符可以是空格,Tab跳格键或是换行符.
2.6.2 标识符(identifiers)
标识符是赋给类,方法或是变量的名字.一个标识符可以是大写和小写字母,数字,
下划线,美元符号的任意顺序组合,但不能以一个数字开始.否则容易与数字,常量相混
淆.再次强调一下,Java是区分大小写的,VALUE和Value是两个不同的标识符.下面是一
些有效的标识符:
AvgTemp count a4 $test this_is_ok
下面是一些无效的变量名:
2count high-temp Not/ok
2.6.3 常量(literal)
在Java中,常量用literal表示.例如,下面是一些常量:
100 98.6 'X' "This is a test"
从左到右,第一个表示一个整数,第二个是浮点值,第三个是一个字符常数,最后是
一个字符串.常量能在任何地方被它所允许的类型使用,代表的是所属类型的一个值.
2.6.4 注释(comments)
Java定义了3种注释的类型.其中2种注释类型你已经知道了:单行注释和多行注释.
第3种注释类型被称为文档注释(documentation comment).这类注释以HTML文件的形式
为你的程序作注释.文档注释以"/**"开始,以"*/"结束.在附录A中对文档注释作了
解释.
26 第1部分 Java语言
2.6.5 分隔符(separators)
在Java中 ,有一些字符被当作分隔符使用,最常用的分隔符是分号(;),用来分隔语
句.下面是常用的分隔符.
符 号 名称 用途
() 圆括号 在定义和调用方法时用来容纳参数表.在控制语句或强制类型转换组成
的表达式中用来表示执行或计算的优先权
{ } 花括号,大括号 用来包括自动初始化的数组的值.也用来定义程序块