Java学习指南

978-7-115-35630-7
作者: 【美】Patrick Niemeyer Daniel Leuck
译者: 李强王建新吴戈
编辑: 陈冀康
分类: Java

图书目录:

详情

本书帮助读者全面地学习和掌握Java开发技能。全书按照示例驱动的方式进行讲解,内容全面,涵盖了Java7最新的语言功能和API。对于中高级的Java读者来说,本书也专门深入介绍了并发编程和JVM方面的内容。本书是Oreilly经典的Java学习指南的最新版。

图书摘要

版权信息

书名:Java学习指南

ISBN:978-7-115-35630-7

本书由人民邮电出版社发行数字版。版权所有,侵权必究。

您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。

我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。

如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。

• 著    [美] Patrick Niemeyer , Daniel Leuck

  译    李 强 王建新  吴 戈

  责任编辑 陈冀康

• 人民邮电出版社出版发行  北京市丰台区成寿寺路11号

  邮编 100164  电子邮件 315@ptpress.com.cn

  网址 http://www.ptpress.com.cn

• 读者服务热线:(010)81055410

  反盗版热线:(010)81055315  


Copyright © 2013 by O’Reilly Media, Inc.

Simplified Chinese Edition, jointly published by O’Reilly Media, Inc. and Posts & Telecom Press, 2014. Authorized translation of the English edition, 2013 O’Reilly Media, Inc., the owner of all rights to publish and sell the same.

All rights reserved including the rights of reproduction in whole or in part in any form.

本书中文简体字版由O’Reilly Media, Inc.授权人民邮电出版社出版。未经出版者书面许可,对本书的任何部分不得以任何方式复制或抄袭。

版权所有,侵权必究。


本书是畅销Java学习指南的最新版,详细介绍了Java 6和Java 7的语言特性和API。本书全面介绍了Java的基础知识,力图通过完备地介绍Java语言、其类库、编程技术以及术语,从而成为一本名符其实的入门级图书。

本书加入了从Java 6和Java 7发布以后的变化,包括新的语言功能、并发工具(Fork-Join框架)、新的NIO Files API、Java Servlet (3.0)等新主题,作者通过精心挑选的、富有实用性和趣味性的示例,进行细致深入的讲解。本书的最后一部分,将Java放入到Web应用程序、Web服务和XML处理的环境中进行了介绍。

本书适合Java语言初学者阅读,对于有一定经验的Java程序员,也可以作为了解Java 7的编程手册参考。


本书讨论的是Java编程语言和编程环境。无论你是一位软件开发人员,抑或仅仅在过去数年经常上网,无庸置疑,Java对你而言都可谓耳熟能详。Java的引入在Web的发展历史中堪称最精彩的手笔之一,并且在过去的15年里,Java应用程序对Internet上的业务的增长贡献很大。Java有可能已成为世界上最流行的编程语言,使得数百万开发人员在几乎所有的计算机上使用它。在过去的十年里,在开发人员的需求方面,Java已经超越了C++和Visual Basic,而逐步成为某些应用种类开发(特别是基于Web的应用和服务)的事实标准语言。许多大学都将Java连同其他重要的现代语言一起作为学生的基础课程。没准你正是在课堂上阅读本书呢!

本书将为你提供关于Java的全面的基础知识。第4版力图通过完备地介绍Java语言、其类库、编程技术以及术语,成为一本名符其实的入门级图书。我们将深入地探讨一些有意思的领域,对于其余方面,则至少会浮光掠影地谈到。关于我们概略涉及的领域,O'Reilly & Associates专门出版了其他图书,它们对Java的特定领域和应用提供了更为详尽的信息。

只要有可能,我们便会提供有影响、有实际意义且有趣的例子,而避免简单地对特性进行罗列。这些例子尽管很简单,但对于所能做的工作却给出了充分的提示。我们不打算仅由这些介绍即开发出另一个“超级应用”,不过,我们希望你花费一些时间来完成这些实验,并以此为起点做进一步的思考,以便开发自己的应用。

本书主要面向计算机专业人员、学生、技术人员。如若需要构建实际的应用,而希望得到有关Java语言的第一手经验,那么本书正可满足你所需。这本书还可以作为一本面向对象编程、网络编程、GUI和XML的绝佳教程。在了解Java的同时,你也将同时掌握一种功能强大而且实用的方法来实现软件开发,而这一切从深入理解Java及其API的基础知识开始起步。

Java与C或C++非常相像,如果对于这些语言有一定的编程经验,那么在使用这本书时将有一个更高的起点。即使从未使用C或C++编程,也不必担心。不要过分关注Java和C或C++在语法上的相似性。在许多方面,Java 则与诸如Smalltalk和Lisp等更为动态的语言具有可比性。如果有另一种面向对象编程语言的知识,当然会对你有所帮助,不过可能需要你改变一些观念,同时要摒弃一些原有的习惯。较之于C++和Smalltalk,Java要简单得多。如果你希望通过简洁的例子和个人的实验尝试而有所收获,那么这本书将如你所愿。

本书的最后一部分将Java放入到Web应用程序、Web服务和XML处理的环境中进行介绍,因此,你应该对Web浏览器、服务器和文档等基本思想有所熟悉。

本书实际上是第6版(如果算上更新版和更名版的话),因为本书最初的版本的名称是《Exploring Java》(一本畅销书)。在每一次新版推出时,我们不仅加入了涉及新增特性的新内容,而且还特别注意了对原有内容进行全面的修订和更新,从而将所涉及领域加以综合,并且在书中加入数年来的实际经验和观点。

在最近的几版中,有一个值得注意的调整,即我们弱化了applet的使用,这反映了过去一两年中applet在创建交互式Web页面方面所起到的作用已经弱化。相反,我们大幅扩展了对Java Web应用、Web服务和XML的介绍,这些内容目前已经是成熟的技术了。

我们还介绍了Java的最新发布版(官方正式名称叫做Java Standard Edition (SE) 7,JDK 1.7)的所有重要特性。Sun(在Oracle收购之前,是Java的拥有者)多年来已经多次更改了Java的命名方案。Sun用“Java 2”一词涵盖了Java 1.2版本中所引入的主要的新特性。在第6次发布的时候,Sun从Java的版本从1.4跳到了Java 5.0,但是保留了术语JDK并且保持其命名惯例不变。此后,有了Java 6,而现在到了Java 7。

Java的这些发布反映了一种成熟语言相对较少会有语法的变化,但是会对API和库做显著的更新。我们尝试在本书中捕获这些新的功能并更新每个示例,从而不仅反映Java的当前实践,而且显示其一贯风格。

本书的这个版本进行了非常显著的改写,以便使其内容尽可能地完整和及时。本书加入了从Java 6和Java 7发布以后的变化,而这些都是本书上一版之后所发生的事情。本版包括以下新主题:

本书内容结构大致如下。

如果你也同我们一样有一定的经验,那么无需从头至尾通读全书。如果你确实有颇高造诣,则往往根本不必读此前言。不过,倘若你此时恰好在看这一内容,那么以下建议可供参考。

关于Java有许多在线的信息资源。Oracle公司的Java官方网站是http://java.sun.com,在此可以获取最新新闻、升级软件和各个Java版本。在这里也能找到Java软件开发工具包(SDK),其中包括编译器、解释器以及其他工具。

也可以访问O'Reilly公司的Java站点http://oreilly.com/java,在此能够找到关于O'Reilly公司所出版的Java系列的其他书籍的信息,以及本书的主页链接(http://oreil.ly/Java_4E),其中可以找到本书中所有例子的源代码。

斜体(italic)用来表示:

等宽字体(constant width)用来表示:

等宽黑体(constant width bold)用来表示:

等宽斜体(constant width italic)用来表示:

这个图标表示一个注意,这是正文附近的重要提示边栏。

这个图标表示与附近正文相关的警告。

在正文中,我们还在方法名的后面使用一对空的圆括号,用来将方法和变量以及其他内容区分开来。

在Java资源列表中,我们遵循了Java社群中最常使用的代码约定。类名的首字母大写;变量和方法名的首字母则小写。常量名的所有字母均大写。对于长名,我们没有使用下画线来分隔单词;而是采用了一般的惯例,即将单个单词的首字母大写(除第1个单词以外),然后再将各个单词组合起来。例如:thisIsAvariable、thisIsAMethod( )、ThisIsAClass以及THISISACONSTANT。另外,要注意我们在表示静态和非静态方法时还有所区别。与其他书不同,在此不会写作Foo.bar( )来表示Foo的bar( )方法,除非bar( )方法确实是静态的。

本书是为了帮助你完成你的工作。通常来讲,你可以任意使用本书中的程序和文档。你不需要在这之前联系我们获得使用许可,除非你要复制相当多的程序。比如,你的程序使用了本书中的多段代码,这不需要获取我们的许可。出售或者传播O'Reilly的书籍的示例代码光盘则需要首先获得授权。通过引用本书的内容或者代码来回答问题不需要预先获得许可,但将本书相当多的示例代码合并入你的产品文档则需要获得我们的许可。

我们不要求你一定注明本书的出处,但我们对这种做法表示认可。一个完整的出处说明应当包括标题、作者、出版社和ISBN。比如:“Learning Java, Fourth Edition, by Patrick Niemeyer and Daniel Leuck. Copyright 2013 Patrick Niemeyer and Daniel Leuck, 978-1-449-31924-3”。

如果你觉得你使用的示例代码超出了上文许可的合理使用,请联系我们:permissions@ oreilly.com。

Safari® Books Online(www.safaribooksonline.com)是一种按需求定制的数字图书馆,同时以图书和视频的形式提供来自全球的技术和商业领域前沿作者的专家内容。

技术专业人员、软件开发人员、网页设计人员以及业务或创新专业人员可以使用Safari Books Online作为研究探索、解决问题、学习和认证培训的主要资源。

Safari Books Online为各家公司、政府机构和个人提供了广泛的产品结构和定价计划。订阅者可以通过一个可完整搜索的数据库访问数以千计的图书、培训视频和预出版手稿,包括O’Reilly Media、Prentice Hall Professional、Addison-Wesley Professional、Microsoft Press、Sams、Que、Peachpit Press、Focal Press、Cisco Press、John Wiley & Sons、Syngress、Morgan Kaufmann、IBM Redbooks、Packt、Adobe Press、FT Press、Apress、Manning、New Riders、McGraw-Hill、Jones & Bartlett和Course Technology等出版商的作品。关于Safari® Books Online的详细信息,可以在线访问。

对于本书加以评论或提出问题请联系出版商:

美国:

O’Reilly Media, Inc.

1005 Gravenstein Highway North

Sebastopol, CA 95472

中国:

北京市西城区西直门南大街2号成铭大厦C座807室(100035)

奥莱利技术咨询(北京)有限公司

我们为本书建有一个网页,其中列出了勘误、示例或其他附加信息。网页地址为:http://oreil.ly/Java_4E

要评论本书或询问技术问题,请发送电子邮件到bookquestions@oreilly.com。要了解关于本书、课程、会议和新闻的更多信息,请访问我们的Web站点http://www.oreilly.com。

通过Facebook联系我们:http://facebook.com/oreilly

通过Twitter联系我们:http://twitter.com/oreillymedia

通过YouTube了解我们的动态:http://www.youtube.com/oreillymedia

本书之所以能够出版,离不开许多人的贡献,在此既包括对其前身《Exploring Java》所做的工作,也包括对于目前这版《Learning Java》所做的努力。首先要感谢Tim O'Reilly为我们提供了编写这本书的机会。特别感谢本丛书的编辑Mike Loukides,是他无尽的耐心和丰富的经验方使我们渡过了那些艰难的阶段。还要向Paula Ferguson和John Posner致以谢意,他们充分发挥了自己的组织和编辑才能。特别要感本书的编辑Deb Cameron,没有他不知疲倦的工作,之前的两版不可能完成,也肯定不会有这样流畅的语言。我们很幸运地拥有一个最有经验同时也最为积极的写作小组。

谈到所参考的资料,术语表的第一版取自David Flanagan所著的《Java in a Nutshell》(O’Reilly出版)。此外我们还参考了David书中的类层次图。这些图都是Charles L. Perkins的类似图表为基础的。

另外要感谢Marc Wallace和Steven Burkett,是他们在本书写作过程中通读了本书的原稿,还要感谢我们在华盛顿大学的朋友们。还要感谢Josh Peck,他是本书的最初版本《Exploring Java》的合作者。还要感谢所有那些审阅过本书并回答问题的人:David Flanagan对于泛型部分的帮助;Henry Wong对于并发工具部分的帮助;Jack Shirazi对NIO方面的帮助;Tim Boudreau对于NetBeans方面的帮助;Martin Aeschlimann,Jim Farley以及John Norman对于Eclipse的指导;Ed Howland 对于XML部分的帮助;以及Ian Darwin对于正则表达式方面的帮助(更多例子请查阅Ian所著的《Java Cookbook》(O'Reilly出版)。另外要感谢Ray O'Leary、Mario Aquino和Mark Volkmann所做的审阅工作。最后,要特别感谢我美丽的妻子Ellen Song对我写作本书毫无怨言。


Patrick Niemeyer最早于西南贝尔实验室技术资源部门(Southwestern Bell Technology Resources)任职时开始涉足Oak(Java的前身)。他是Ikayzo公司的CTO,是一位独立的咨询师和作者。Pat曾开发了BeanShell,这是一种流行的Java脚本语言。他是几个JCP专家组的成员,JCP专家组是负责指导Java语言功能的组织;他还是众多开源项目的贡献者。最近,Pat为金融行业开发了分析软件,还开发了高级的移动应用。如今他生活在美国圣路易州的Central West End地区,陪伴他的是他的家庭,还有许多小动物。

Dan Leuck是Ikayzo公司的CEO,这是一家以东京和檀香山为基地的交互设计和软件开发公司,其客户包括Sony、Oracle、Nomura、PIMCO以及联邦政府。他之前担任东京的ValueCommerce公司的负责研发的高级副总裁,这是亚洲最大的营销公司;还曾经担任伦敦的LastMinute.com的全球开发总裁(这是欧洲最大的B2C网站),还曾经担任DML的美国分公司总裁。Dan拥有一个经验丰富的管理团队,该团队在5个国家拥有150名开发者。他还曾担任Macromedia和Sun Microsystems等很多公司的咨询董事和委员。Dan在Java社群很活跃,是BeanShell的贡献者,也是SDL项目的负责人,并且是众多的JCP专家组的成员。


对于当前的软件开发人员而言,要驾驭网络的强大力量不仅是最为艰巨的挑战,同时也是最令人兴奋的机遇。如今创建的应用,无论预期的适用于什么范围或面向何种用户,运行软件的机器往往都会与全球的计算资源网络相连。网络的重要性日渐突出,这不仅对现有的工具提出了新的要求,而且还迫切需要全新的应用得到飞速的发展。

我们希望软件能够在任何位置、任何平台上以一致的方式工作,而且可以与其他应用很好地合作。对于充分利用互联世界的动态应用,我们希望它们能够访问异构和分布式的信息源。我们希望可以无缝地得到扩展和升级的真正的分布式软件。除此以外,我们还希望所连接到网络的智能应用能够搜索出信息,并且充当我们的电子使者。我们早已很清楚自己所要的软件了,可真的直到最近几年才开始努力实现它。

这是历史遗留的问题,其原因在于用于构建这些应用的工具尚有不足。大多数情况下,对于速度和可移植性的需求往往水火不相容,而且安全性也常常被忽视甚至被误解。以前确实存在一些可移植语言,但是它们通常都很庞大,而且是解释性的,因此速度很慢。这些语言因为其高级功能和可移植性而流行。也有一些速度很快的语言,但是它们通常是将自身绑定至某些特定平台,以此来换取速度,因此它们很快就遇到可移植性的问题。此外,最近甚至还出现了少量安全语言,不过它们主要是可移植语言的分支,因此也存在同样的问题。Java是一种现代语言,它解决了前面所提到的所有这3个问题,即可移植性、速度和安全性。这就是为什么在长达15年的时间里,它一直能够成为编程世界中的一种主流语言。

Java编程语言是在网络泰斗James Gosling和Bill Joy的指导下由Sun公司开发的,其目标是要将其设计为一种与机器无关的编程语言,不仅能够做到足够的安全从而在网络上“畅行无阻”,而且功能要非常强大,从而足以替换本地可执行代码。Java可以解决这里提出的问题,而且有助于我们着手构建所需的新型应用。

最开始的时候,对于Java的热情几乎都倾注于它能够为Web构建一种称为applet的嵌入式应用。但是在早期,用Java编写的applet和其他客户端GUI应用很有限。如今,Java拥有了Swing,这是所有语言中用于构建图形用户界面的最为复杂的工具包之一。这一发展使得Java得以成为开发传统客户端应用软件的一个流行平台。

更为重要的是,Java也成为了Web应用和Web服务的主流平台。这些应用程序所使用的技术包括Java Servlet API、Java Web Services,以及众多流行的开源或商业的Java应用程序服务器和框架。Java的可移植性和速度,使其成为现代分布式应用的首选平台。运行在开源Linux平台上的Java服务器,已经深入当今商业和金融行业的核心。

本书将向你展示如何使用Java来完成实际的编程任务。在后续的各章中,我们将依次介绍所有内容,从文本处理到网络,使用Swing构建富客户端GUI程序,以及轻量级的Web应用程序和服务等。

Java的最初思想是在1990年由Sun Microsystems公司的创办人和首席研究员Bill Joy提出来的。在当时,Sun只在小型工作站市场上享有相当小的份额,而Microsoft公司则在基于Intel的PC主流领域占据着主导地位。Sun公司错过了PC革命的浪潮,Joy回到了科罗拉多州的Aspen开始进行深入研究。他致力于用简单的软件来完成复杂的工作,并创建了一个名为Sun Aspen Smallworks的软件。

在Aspen工作的最初几个程序员当中,James Gosling后来被称为Java之父。Gosling在20世纪80年代早期开发了一个用自己名字命名的软件Gosling Emacs,这是流行编辑器Emacs的第一个用C语言编写并在Unix下运行的的版本。Gosling Emacs开始变得相当流行,但是不久被一个免费的版本GNU Emacs所取代,后者是由Emacs的最初设计者所编写的。那时,Gosling已经转而设计Sun公司的NeWS窗口系统,在1987年这个系统主要是为了与X Window系统争夺对UNIX GUI桌面系统的控制。尽管一些人认为NeWS较X Window更好,但是NeWS最后却不得不败北,这是因为Sun独占了NeWs的所有权,而且没有公开其源代码,而X Window的主要开发者则组建了X Window联盟并采取了和Sun相反的策略。

通过设计NeWS,Gosling认识到,将与网络有关的窗口GUI同描述性语言相集成有着何等的威力。另外Sun也意识到Internet编程群体最终对专有标准都将不予认同,而不论它多么优秀。正是NeWS的失败,促使Gosling萌生了Java卓越的授权许可机制的思想。Gosling把得到的宝贵经验带到了Bill Joy的最初的Apsen项目中。在1992年,这个项目导致了Sun的子公司FirstPerson公司的成立。该公司的任务就是领导Sun进入消费电子领域。

FirstPerson小组主要致力于信息应用软件的开发,如蜂窝电话和个人数字助理(PDA)软件等。其目标是通过廉价的红外线和传统的基于数据包(packet)的网络来传输信息并实现实时应用程序。内存和带宽的限制使得只能使用小而高效的代码,而应用的本质又要求代码必须做到安全而健壮。Gosling和小组成员最初尝试用C++ 来编写,不过很快他们就被这种复杂、笨拙而且不安全的语言搞得一头雾水。他们决定从头做起,重新编写一个语言,Gosling将这种语言戏称为“C++ minus minus”。

Apple Newton的推出,表明当时还不是PDA面市的时机,因而Sun将FirstPerson的目标转向到交互式TV(ITV)上。设计ITV机顶盒所选用的编程语言即为Java的雏形,称作Oak。尽管这种语言很优雅,也能提供安全的交互性,但是Oak依然不能挽救ITV的失败,消费者根本就不需要它,不久Sun也放弃了这个项目。

此时,Joy和Gosling聚在一起为他们的语言确定新的战略。到了1993年,随着人们对Internet兴趣的爆炸式增长,特别是对Web的关注简直无可比拟,这就提供了一个新的机遇。Oak是一种小型的、健壮的、体系结构独立而且面向对象的语言。而且当时恰逢急需一种适用广泛而且支持网络的编程语言。Sun很快调整了重心,仅对Oak稍加改动,即成为了后来的Java。

说Java就像野火一样蔓延开来并不过分。甚至在还未公布其第一个正式版本之前,尽管当时Java还不能算是一个产品,几乎所有重要的厂商就都已在迫不及待地追赶着Java潮流了。获得Java授权的公司包括Microsoft、Intel、IBM以及几乎所有主要的硬件和软件厂商。但这并不意味着一切将一帆风顺。即使有如此众多的支持,在最初的几年里,Java仍然经历了很多挫折和阵痛。

由于Sun和Microsoft之间就Java的发布及其在Internet Explorer中的使用而争论不休,这就阻碍了Java在Windows这一世界上最通用的操作系统上的部署。Microsoft对于Java的态度也成为一个更激烈的行业诉讼的焦点,甚至超过了Microsoft与反垄断相关的法律诉讼,而Microsoft在法庭上的陈述则表示了协商努力的结果是:这个软件巨头力图通过在其Java版本中引入不兼容性以削弱Java的发展势头。与此同时,Microsoft还推出了它自己的类Java语言,称为C#(C-sharp),以此作为其.NET项目的一部分。C#自身已经发展成为一种很好的语言,近年来甚至有比Java更多的创新。

但是Java继续在广泛的平台上扩展。在开始了解Java体系结构的时候,你将看到,Java大多数激动人心之处源自于Java应用运行所在的自包含虚拟机环境。Java得到了精心设计,从而使得这一支持性的架构可以针对现有计算机平台而使用软件实现,也可以采用定制硬件来实现。Java的硬件实现用于一些智能卡和其他嵌入式系统中。你甚至可以购买其中嵌入了Java解释器的“可穿戴”设备,例如,戒指和狗牌。Java的软件实现则可供所有现代计算机平台使用,甚至下沿到便携计算机设备。如今,Java平台的一个分支成为Google的Android操作系统的基础,而Android已经安装到上百万台的手机和其他移动设备上。

2010年,Oracle收购了Sun Microsystems并且成为Java语言的管理者。在Oracle刚开始其任期的不平凡的日子里,Oracle要求Google停止在Android中使用Java语言,但最终失败了。2011年7月,Oracle发布了Java SE 7,这是一个重要的Java发布版本。

Java既是一种编译语言也是一种解释语言。Java源代码将被转换为简单的二进制指令,这与通常的微处理器机器码非常类似。不过,C或C++ 源代码要针对特定处理器模型而优化为本地指令,而Java源代码却均会编译为一种通用格式,即面向虚拟机的指令。

已编译Java字节码由一个Java运行时解释器执行。运行时系统可以完成一个实际处理器所做的所有正常操作,但是会在一个安全的虚拟环境中完成这些工作。它会执行一个基于栈的指令集,并像操作系统一样管理内存。运行时系统将创建并操作基本(primitive)数据类型,另外加载和调用新近引用的代码块。最重要的是,所有这些都是遵循一个严格定义的开放规范完成的,任何人如若希望生成一个与Java兼容的虚拟机,都可实施此规范。综合来看,虚拟机和语言定义则提供了一个完整的规范。这样,基本Java语言的所有特性都得到了义,而且与实现无关。例如,Java指定了所有基本数据类型的大小,而不是将其留待平台实现来确定。

Java解释器是相对轻量级的,而且规模较小,它可以实现为某种特定平台所需的任何一种形式。解释器可以作为一个单独的应用运行,也可以嵌入到另一个软件中(如Web浏览器)。综合起来看,这意味着Java代码隐含的就是可移植的。同样的Java应用程序的字节码,可以运行在提供了Java运行时环境的任何平台上,如图1-1所示。你不需要为了不同的平台而提供应用程序的其他版本,并且你不必把源代码分发给用户。

图1-1 Java运行时环境

Java代码的基本单位是类。与其他面向对象语言类似,类是包含可执行代码和数据的应用组件。已编译的Java类采用一种通用的二进制格式发布,其中包含有Java字节码和其他类信息。类可以分散地加以维护,并保存于本地的文件或归档文件中,也可以保存在网络服务器上。应用需要类时,将在运行时动态地进行定位和加载。

除了特定于平台的运行时系统外,Java还有大量的基本类,其中包含有依赖于架构的方法。这些本地方法(native method)相当于Java虚拟机和真实世界之间的网关。它们在主机平台上以一种本地编译语言实现,并提供对网络、窗口系统和主机文件系统等资源的低层级访问。然而,Java的主要部分则完全用Java编写的,都是从这些基本部分演化而来,因此是可移植的。这包括基本Java工具,如Java编译器、网络和GUI库,它们都采用Java编写,因此在所有Java平台上均可以下同的方式使用而不需要移植。

基于历史的原因,往往认为解释器很慢,但是由于Java并非传统的解释语言。除了将源代码编译为可移植的字节码,Java还得到了精心的设计,通过将字节码动态编译为本地机器码,使得运行时系统的软件实现可以对其性能进行优化。这也称为即时(Just-In-Time,JIT)编译或动态编译。利用JIT编译,Java代码可以与本地已编译代码执行得一样快,同时还可保持其可传输性和安全性。

对于力图比较语言性能的人来说,这往往是一个误区。已编译Java代码只是由于安全性和虚拟机设计(数组越界检查)而导致运行时性能有所下降,而这也是性能下降的唯一的内在因素。而其他的所有内容都可以优化为本地码,这一点与静态编译语言所能做的并无二致。除此之外,较之于其他语言,Java语言还包括了更多的结构化信息,这样就为优化提供了更大的空间。还要记住,这些优化可以在运行时完成,即可以考虑到实际的应用特性。那么什么工作更适合于在编译时完成而不是运行时完成呢?在此有一个权衡,即时间。

传统JIT编译的问题在于,优化代码会花费时间。因此,JIT编译器可以得到很好的结果,但是,在应用程序启动的时候会有显著的延迟。对于长时间运行的服务器端应用程序来说,这通常不是个问题,而对于在计算能力有限的较小设备上运行的客户端软件和应用程序来说,这却是一个严重的问题。为了解决这个问题,Java的编译器技术(称为HotSpot)使用了一种叫做自适应编译(adaptive compilation)的技巧。如果仔细查看程序究竟花费时间做了哪些工作,可以发现它们往往会把绝大部分时间用于反复执行一部分代码。这一反复执行的代码块可能仅仅是整个程序中很小的一部分,但是其表现却决定了整个程序的性能。自适应编译还允许Java运行时利用一些新的类型的优化,而这些优化在一种静态编译型语言中是无法做到的,因此,Java代码号称在某些情况下可以比C/C++运行得更快。

为了充分利用这一事实,HotSpot仍以一个正常的Java字节码解释器“出场”,但在此存在一个区别:它在执行代码时要对代码进行测量(探查),从而查看哪些部分得到了重复执行。一旦获知对于性能有关键影响的代码部分,HotSpot将把这些代码段编译为真正的机器码。由于它只是将程序的一小部分编译为机器码,因此就有足够的时间对这一部分加以优化。程序余下的部分则根本无需编译,只要进行解释即可,这样可以节省内存和时间。实际上,Java VM(虚拟机)可以以两种模式运行:客户端和服务器,这分别决定了重点究竟是快速的启动时间以及节省内存还是最佳的性能。

此时,一个自然而然的问题是,为何每次一个应用程序关闭的时候,要丢弃这些很好的探查信息?Sun在发布Java 5.0的时候,将共享的、只读的类持久地存储到一个优化后的表单中,从而部分地回答了这个问题。这显著地减少了启动时间,以及在一台给定的机器上运行多个Java应用程序的负担。实现这一点的技术很复杂,但是,思路很简单:即优化程序中需要快速运行的部分,而不需要担心其他的部分。

Java虽是一种新语言,但是在其特性选择中,还汲取了其他语言多年以来的编程经验。花点时间来在较高的层面比较Java和其他语言是值得的,不管你是具有其他编程经验,还是需要搞清楚状况的新手,这都是有好处的。在本书中,我们不要求你具备任何特定编程语言的知识,并且当我们通过比较而提及其他语言的时候,我们希望这些说明是简单而直白的。如今一种通用语言至少要支持以下3点:可移植性、速度和安全性。图1-2显示了Java与其他几种流行语言的比较情况。

图1-2 编程语言之间的比较

“Java非常类似于C或C++”,对此你可能早有耳闻,但除表层以外,事实却并非如此。你第一次看到Java代码时,将会看到其基本语法确实与C或C++ 很相近。但是它们的相似性也仅限于此。Java绝对不是C的一个换代产品,也不是C++的下一版本。如果对语言特性加以比较,可以看出Java与Smalltalk和Lisp等语言有着更多的共同之处。实际上,Java的实现与本地C相比真可谓有着天壤之别。

如果你熟悉当前的语言的发展情况,你将会注意到,C#这种流行的语言没有在比较之中。C#在很大程度上是Microsoft对Java的应战,应该承认C#有很多优点超越了Java。这两种平台具有一些共同的设计目标和方法(例如,使用虚拟机、字节码和沙箱等),而在其速度和安全特性方面也没有太大的差别。理论上讲,C#和Java一样可移植,但迄今为止支持它的平台要比Java少很多。和Java一样,C#在很大程度上也借鉴了C的语法,但是,实际上它更接近于动态语言。大多数Java开发者发现很容易学习C#,反之亦然。从其中一种语言迁移到另一种语言的大部分时间,都花在了学习标准库上。

不过,对于这些语言之间的表面级的相似性还是有必要加以说明。Java大量借用了C和C++ 的语法,因此你会看到许多熟悉的语言构造,其中包括反复出现的大括号和分号。Java还采纳了C的观点,即一个好的语言应当是简洁的;换句话说,它应当足够小而且相当规范,从而使程序员可以立即掌握该语言的所有功能。正如C利用库来加以扩展,也可以将Java类包添加到其核心语言组件中。

C已广获成功,这是因为它提供了一个特性相当丰富的编程环境,不仅性能很高,而且还有一定的可移植性。Java也尝试在功能、速度和可移植性三者之间取得平衡,但是它所采用的方式却截然不同。C是以功能换取可移植性;而Java则是靠牺牲速度来得到可移植性。另外,Java还解决了安全性问题,对此C则束手无策(尽管在现代系统中,很多安全问题都在操作系统和硬件中解决了)。

在早期阶段,即JIT和自适应编译尚未提出以前,Java相对于编译型语言要慢得多,并且批评者经常挂在嘴边的说法是,Java的速度永远也赶不上静态编译语言。但是正如前一节所述,目前对于同样的任务,Java与C或C++ 的性能完全可以比拟,并且那些批评也渐渐销声匿迹。ID软件的开源视频游戏引擎Quake2已经移植到Java平台上。如果Java对于第一人称战斗视频游戏来说已经够快了,那么,对于商业应用来说,它肯定也够快了。

诸如Perl、Python和Ruby这样的脚本语言越来越流行。尽管脚本语言对于安全的网络化应用并非完全不适合,但是大多数脚本语言都不是为严格的大型编程而设计的。脚本语言的诱人之处在于它们的动态性;对于快速开发,脚本语言则是功能非常强大的工具。

有一些脚本语言(如Perl)还提供了很强大的工具来完成文本处理任务,对此更通用的语言往往无法胜任。脚本语言也具有很好的可移植性,即使是在源代码的层级。

不过,不要将Java与JavaScript混为一谈!JavaScript是由Netscape等公司合作开发的一种基于对象的脚本语言。对于动态、交互式的Web的应用来说,它充当浏览器的本地语言。JavaScript的名字源自于它与Java的结合以及类似性,但仅此而已。尽管在浏览器之外也有了JavaScript编写的应用程序,但它还没有真正成为一种通用性的脚本语言。有关JavaScript的更多信息可以参阅David Flanagan所著的《JavaScript: The Definitive Guide》(由O'Reilly出版)。

不过脚本语言也存在一个问题,即它们对程序结构和数据类型有些过于随意。大多数脚本语言都不是面向对象的(Perl的最新版本和Python例外)。其类型系统也太过简化,而且无法提供复杂的变量和函数的作用域控制。这些特点使之无法适用于构建大型、模块化的应用。脚本语言的另一个问题在于速度,这些高级语言具有完全的解释性,这就使得其速度相当慢。

个别脚本语言的支持者可能对这些一概而论持不同意见,当然有些情况下他们是对的。脚本语言近年来也在改进,尤其是JavaScript,在其性能方面投入了大量的研究。但是,最根本的考虑是不可否认的:脚本语言是作为系统编程语言的一种松散的、缺乏结构的替代而产生的,由于众多的原因,对于较大的或复杂的项目来说,一般不是理想的选择,至少目前还不是。

Java提供了脚本语言的一个基本优点,即高度动态化,另外还增加了低级语言的一些额外优势。Java有一个强大的正则表达式API(Regular Expression API),从而使Java在处理文本方面可以与Perl媲美,此外,对集合的流编码、可变参数列表、方法的静态导入以及其他的语法糖等语言特性,也使得Java更为精炼。

一方面利用面向对象组件的递增开发,另一方面再结合Java的简单性,这样就可以快速地开发应用,并且能够很容易地加以调整。许多用户发现,只要严格地基于语言特性,较之于使用C或C++ 来说,用Java开发要快很多。Java还为诸如构建GUI和完成网络通信等公共任务提供了一个大型核心类库。另外基于这些特性,Java还具有可伸缩性和软件工程特性,而这些特性本是更为静态的语言的优点。Java还提供了一个安全的结构,在此之上可以构建更高级的框架(甚至其他语言)。

如前所述,Java在设计上与Smalltalk和Lisp等语言存在相似性。不过,这些语言目前大多用作研究工具,而不是用来开发大型系统。其中一个原因是,它们从未开发针对操作系统服务的一种标准可移植绑定,如C标准类库或Java核心类等。Smalltalk编译为一种已解释字节码格式,而且可以动态地编译为本地码,这一点与Java相同。但是Java在设计上有所改进,即使用了一个字节码校验器以确保已编译Java代码的正确性。此校验器使Java的性能优于Smalltalk,因为Java代码需要更少的运行时检查。Java的字节码校验器还有助于解决安全性问题,对此Smalltalk则未有建树。

在本章余下的部分,我们将对Java语言做一个全面的概述。在此将说明哪些是Java的新内容,哪些则是借用的原有内容,并解释为什么存在这些差别。

Java被设计为一种安全语言,对于这一事实你肯定早已耳熟能详了。但是在此“安全”指的是什么呢?对什么而言安全,或者对谁安全呢?对于Java,得到颇多关注的安全性是那些使新型动态可移植软件成为可能的有关特性。Java提供了多层保护以避免恶意代码,并防止诸如病毒和特洛伊木马等更具危险性的东西。在下一节中,我们将查看Java虚拟机体系结构如何在代码运行前评估其安全性,还将介绍Java类加载器(Java解释器的字节码加载机制)如何在不可信类周围加筑围墙。这些特性为高级安全性策略提供了基础,从而可以在每个应用的基础上允许或禁止各种操作。

不过,在本节中,我们将了解Java编程语言的一些通用特性。较之于特定的安全特性,Java通过解决通用设计和编程问题所提供的安全性可能更为重要,但在安全性讨论中这一点往往被忽视了。Java力图做到尽可能安全,即不仅要“抵制”我们自己所犯的简单错误,而且还要避免由原有软件所遗传的错误。Java的目标是保持语言的简单性,并提供展示其有用性的工具,同时令用户可以在需要时基于该语言构建更为复杂的功能。

Java有着简单性的原则。因为Java出身清白,它可以避免那些在其他语言中已经证实为糟糕或有争议的那些特性。例如,Java不允许程序员自定义操作符重载(overloading),而在某些语言中,允许程序员重新定义+和-这样的基本操符号的含义。Java没有源代码预处理器,因此没有宏、#define语句或条件源编译。这些在其他语言中存在的构造主要是为了支持平台依赖性,因此从这个意义上讲,它们在Java中是不需要的。条件编译通常还用于调试,但是Java的高级运行时优化以及断言这样的功能,较为优雅地解决了该问题。(我们将在第4章中讨论有关内容)。

Java为组织类文件提供了一个定义良好的包结构。此包系统允许编译器处理传统make实用工具的某些功能(make是用于将源代码构建为可执行代码的一个工具)。编译器还可以直接处理已编译Java类,因为所有类型信息都得到了保留;在此无需“头文件”,这一点与C或C++ 有所不同。所有这些都意味着Java代码需要读取的上下文环境信息更少。实际上,你有时可能会发现查看Java源代码比参考类文档更为快捷。

对于在其他语言中遭遇麻烦的一些特性,Java则将其取而代之。例如,Java只支持单一的类继承层次体系(每个类只能有一个“父”类),但是允许对接口多重继承。接口类似于C++ 中的一个抽象类,可以指定一个对象的多个操作,但是不会定义其实现,这是一个功能强大的机制,它允许开发者为对象定义一个“契约”,任何具体的对象实现都可以使用并引用该契约。Java中的接口消除了类的多重继承需求,同时不会导致与多重继承相关的问题。在第4章中你将会看到,Java是一种简单而又优雅的编程语言,而这仍然是它最大的吸引力。

语言的一大属性是其采用何种类型检查。一般地,在将一种语言划归为“静态”或“动态”时,我们所指的是:有关变量类型的信息究竟是在编译时更多地得到明确,还是直至应用运行时方能更多地加以确定。

在诸如C或C++ 这样的严格静态类型语言中,数据类型在编译源代码时即已固化。这有利于编译器得到足够的信息,从而在代码执行前就能捕获多种错误,例如,编译器不会允许你在一个整数变量中保存一个浮点值。这样,代码将不再需要运行时类型检查,因此可以编译为小而快速的可执行代码。但是静态类型语言不够灵活。它们不能支持诸如集合的高级构造,而这些构造对于带有动态类型检查的语言则相当自然,另外对于静态类型语言而言,应用在运行时也不可能安全地导入新的数据类型。

与此相反,诸如Smalltalk或Lisp等动态语言则有一个运行时系统,可以管理对象的类型,并在应用执行时完成必要的类型检查。这些语言允许更为复杂的操作,另外在许多方面,其功能也更为强大。不过,它们往往速度较慢,不太安全,同时也较难调试。

语言之间的差别可以比作不同汽车之间的差别。静态类型语言(如C++)可以比作跑车,相当安全,速度也很快,但是只有在柏油大道上才能很好地奔驰。动态性很好的语言(如Smalltalk)则更像是越野车:它们可以提供更大的自由度,但是稍难操控。也许在丛林里驾驶着它驰骋相当有趣(有时也更快),但是有时则未免会陷入壕沟或者遭到熊的袭击。

语言的另一个属性是采用何种方式将方法调用绑定至其定义。在诸如C或C++这样的语言中,方法的定义通常在编译时绑定,除非程序员特别指出。Smalltalk则有所不同,它被称为是一种“延迟绑定”(late-binding)语言,因为它在运行时才会动态地确定方法的定义。出于性能方面的原因,早期绑定(early-binding)相当重要;如此可以运行应用,而不会有运行时搜索方法所带来的开销。但是延迟绑定更为灵活。另外在面向对象语言中,这也是必要的,在此子类可以覆盖其超类中的方法,而且只有运行时系统才能确定应当运行哪个方法。

Java博采了C++ 和Smalltalk的优点,它是一种静态类型、延迟绑定的语言。Java中的每个对象都有一个编译时即已确定的定义良好的类型。这说明,Java编译器可以像是在C++中一样,完成同样的静态类型检查和使用分析。因此,你无法给对象赋予错误的变量类型,也不能在一个对象上调用不存在的方法。更有甚者,Java编译器还可以防止使用未初始化的变量以及创建不会执行的语句(请见第4章)。

不过,Java同时也完全可以做到在运行时确定类型。Java运行时系统会跟踪所有对象,并使得在执行时确定其类型和关系成为可能。这说明,可以在运行时检查一个对象以确定它究竟是什么。与C或C++不同的是,将一种对象类型强制转换为另一种类型时,要由运行时系统加以检查,而且有可能使用新型的动态加载对象(具有一定类型安全性的)。另外,由于Java是一种延迟绑定语言,一个子类总是有可能覆盖其超类中的方法,即使这是一个运行时加载的子类。

Java从其源代码中将所有数据类型和方法签名信息带入到其编译后的字节码形式中。这就意味着,Java类可以递增地进行开发。你自己的Java类也可以安全地与来自于其他来源(编译器从未见过此来源)的类一同使用。换句话说,可以编写新的代码来引用二进制类文件,而不会丢失从源代码所得到的类型安全性。

困扰C++ 的一个常见问题是“脆弱基类”问题(fragile base class)。在C++ 中,由于一个基类有多个派生类,因此其实现可能被有效地“冻结”了;修改基类可能需要重新编译所有的派生类。对于类库的开发人员来说,这个问题尤其困难。Java通过在类中动态地定位字段,从而避免了这一问题。只要类维护了其原始结构的一个合法形式,那么就可以对其加以改进,而不会对由该类派生或使用了该类的其他类造成破坏。

Java和C(C++)这样的低级语言之间的一些最为重要的差别涉及到Java如何管理内存。Java取消了可以引用内存的任意部分的临时的指针,并且为语言增加了垃圾回收和高级数组。这些特性消除了有关安全性、可移植性和优化的许多问题,否则这些问题将很难解决。

垃圾回收本身就可以使无数的程序员免于进行显式的内存分配和释放,而这在C或C++ 中也最容易导致错误。除了在内存中维护对象外,Java运行时系统还记录了对这些对象的所有引用。只要某个对象不再使用,Java即会自动地将其从内存中删除。你只需在不再使用对象时将其忽略,并确信解释器在适当的时候会予以清除。

Java使用了一个复杂的垃圾回收器,它在后台间歇性地运行,这意味着大多数垃圾回收工作均发生在空闲时间里,即介于I/O暂停、鼠标点击或按键之间。高级的运行时系统(如HotSpot)则可完成更高级的垃圾回收工作,甚至可以区分对象的使用模式(如对短期对象和长期对象加以区别),并且可以优化其收集过程。Java运行时现在可以自动调整自身,以便针对不同的应用程序,根据其行为来优化内存的分配。通过这种运行时探查,自动化的内存管理比最勤奋的程序员所管理的资源也要快很多,而某些老派的程序员仍然对此难以置信。

你可能听说过Java没有指针。严格地说,这种说法是正确的,但是它也会带来误导。Java所提供的是引用(reference),这是一种“安全型”指针,而且在Java中,引用是相当普遍的。引用是对象的一个强类型句柄。除了基本数字类型之外,Java中的所有对象都可以通过引用来访问。如果必要的话,可以使用引用来构建所有一般的数据结构,如链表、树等等,对于这些数据结构,以往C程序员惯用的做法是采用指针来构建。唯一的区别在于利用引用必须以一种类型安全的方式来操作。

在引用和指针间还有一个重要的区别,即无法通过引用更改其值(执行指针的算术运算)。引用只能指向特定的对象或某个数组中的元素。引用是一个原子性事物;除非将引用赋给一个对象,否则无法操作引用的值。引用采用传值方式传递,而且引用一个对象时,间接层不能多于一层。对引用的保护是Java安全性中最基本的一个方面。这说明,Java代码必须“按规章办事”,即不得“越权”行事。

不同于C或C++ 指针,Java引用只能指向类的类型。在此不存在指向方法的指针。人们有时会对此有所抱怨,但是你会发现,若任务需要方法指针,那么大多数时候,采用接口和适配器类会更为漂亮地将其完成。另外还需提到一点,Java有一个复杂的“反射(reflection)”API,这确实允许你引用和调用单个的方法。不过,这并不是常规做法。我们将在第7章讨论反射。

最后,Java中的数组是真正的头等(first-class)对象。它们可以像其他对象一样动态地分配和赋值。数组知道其自己的大小和类型,而且尽管你无法直接定义或派生数组类的子类,但是基于其基类型的关系,它们确实有一个定义良好的继承关系。语言中若拥有真正的数组,则可以消除C或C++等语言中对指针算术运算的需求。

Java的出发点在于网络化设备和嵌入式系统。对于这些应用,拥有健壮而且智能的错误管理机制是至关重要的。Java有一个强大的异常处理机制,这一点有些类似于C++ 的最新实现。异常提供了一个更为自然和优雅的方式来处理错误。异常可以将错误处理代码从一般的代码中分离出来,从而得到更为简洁、更具可读性的应用。

出现一个异常时,将导致程序执行流程转移到一个提前指定的“捕获”代码块。异常附带有一个对象,其中包含有导致出现异常的情形的相关信息。Java编译器要求方法所声明的异常要么是其能够生成的,要么是可以自行捕获和处理的。这将错误信息的重要性,提高到与参数(argument)和返回类型相同的层次。作为一个Java程序员,应当清楚地知道哪些异常情况需要处理,而且编译器还有助于你编写正确的软件,从而不会让这些异常“放任自流”而未加处理。

如今的应用都需要高度的并行性。即使一个非常简单的应用也可能有一个复杂的用户界面,而这就需要并发的活动。随着机器速度越来越快,用户对于为完成无关任务而占用时间的现象也越来越敏感。线程为客户和服务器应用提供了高效的多处理和任务分配机制。Java使得线程很易于使用,因为其对线程的支持是内置于语言中的。

并发性固然很好,但是采用线程编程所做的不仅仅是同时完成多项任务。在大多数情况下,线程需要得到同步(协调),如果没有显式的语言支持则会相当棘手。Java基于监视器和条件模型可以支持同步,这是一种用于访问资源的加锁和钥匙系统。关键字synchronized指定方法和代码块要在对象内得到安全、串行化的访问。也存在一些简单的基本方法,从而可以在对同一对象加以处理的线程之间显式地等待和标记。

Java还有一个高级的并发包,它提供了强大的工具来解决多线程编程中的常见模式,例如,线程池、任务的协调以及复杂的锁定。通过这个并发包和相关的工具,Java提供了一些比任何其他语言都更为高级的线程相关工具。

尽管一些开发者可能永远不必编写多线程代码,但学习使用线程编程,是掌握Java编程的一个重要部分,这是所有程序员都应该掌握的内容。参见第9章关于这个主题的更多讨论。

在最低的层次上,Java程序由类组成。类被设计为小型的模块化组件。在类之上,Java提供了包,这是一个结构层,它将类分组为功能单元。包为类的组织提供了一个命名约定,另外还对Java应用中变量和方法的可见性提供了另一级组织控制。

在一个包中,类可以是公开可见的,也可能有所保护以避免外部访问。包构成了另一种类型的作用域,它与应用级更为接近。这有助于构建能够在系统中协同工作的可复用组件。包还有助于设计一个可伸缩的应用,从而在扩展应用时,代码不至于过于相互依赖。

创建一种语言从而使自己免于自我伤害,这是一回事;而创建一种语言避免别人攻击你则是另一回事。

封装(encapsulation)是一种将数据和操作隐藏于类中的概念,它是面向对象设计中的重要部分。它将有助于编写简洁的模块化软件。不过,在大多数语言中,数据项的可见性只体现为程序员和编译器之间关系的一部分,这是一个语义问题,而并非数据在运行程序环境中实际安全性的断言。

Bjarne Stroustrup在C++ 中选择了关键字private来指定类的隐藏成员,他可能考虑到了令你免于陷入某个类开发人员所写代码的繁杂细节中,但是却没有考虑到另一个问题,即保护该开发人员的类和对象免于遭受其他人的病毒和特洛伊木马等的攻击。在C或C++ 中可以进行任意的类型强制转换和指针算术运算,这就使得很容易违反类的访问许可,而并不违反该语言的规则。请考虑以下代码:

在这段C++的代码,我们编写了一些违反Finances类封装性的代码,并窃取了一些秘密信息。这种诡计(即滥用无类型指针)在Java中是不能得逞的。如果这个例子还显得有些不切实际,请设想一下,保护运行时环境的基类(系统类)免受类似攻击该是何等重要。如果不可信代码可以破坏对实际资源(如文件系统、网络或窗口系统等)提供访问的组件,那么它自然有机会窃得你的信用卡号。

如果一个Java应用要从Internet上的某个不可信来源动态地下载代码,并且将它与可能包含机密信息的其他应用一同运行,那么就必须尽可能加大保护的力度。Java安全模型在所导入类周围包装了3个保护层,如图1-3所示。

图1-3 Java安全模型

在外层,应用级安全决策由一个安全管理器做出。安全管理器控制对文件系统、网络端口和窗口环境的这样的系统资源的访问。安全管理器要依赖于类加载器的功能来保护基本的系统类。类加载器负责处理从本地存储或网络加载的。在内层,所有系统安全性最终都要取决于Java校验器,它将确保导入的类的完整性。

Java字节码校验器是Java运行时系统中的一个固定部分。而类加载器和安全管理器(或者更确切地说,应称为安全策略)则是可以有不同实现的组件,即对于加载字节码的不同应用(如applet浏览器和Web浏览器)来说,可以用不同的方式来实现类加载器和安全管理器。以上三者都必须正常工作才能确保Java环境的安全性。

Java的第一道防线是字节码校验器。校验器在字节码运行前先读取它,并确保其行为正常而且遵循Java语言的基本原则。一个可信的Java编译器是不会生成反常的字节码的。不过,有恶意的人也有可能故意组合出不好的代码,对此加以检测正是校验器的任务。

一旦代码得到校验,就可以认为是安全的,即不存在某些偶然或恶意的错误。例如,得到校验的代码不会仿造引用或违反对象的访问许可(就像前面举的信用卡的例子那样)。它也不会完成非法的类型强制转换或者以非法的方式使用对象。此代码甚至不会导致某些内部错误,如操作数栈上溢出或下溢出。这些就构成了Java安全性的坚实基础。

你可能会有所疑问,许多解释语言不是也隐含地有这种安全性吗?不错,确实如此,例如你无法借助恶意的BASIC代码来破坏解释器,但是要记住,大多数解释语言中的保护都发生在一个更高层次上。这些语言往往有重量级的解释器,它们要完成大量运行时工作,因此无可避免地会较慢,也较为笨重。

比较而言,Java字节码则是相对轻巧的低级指令集。在Java字节码执行前能够对其静态地加以校验,这就使得Java解释器可以全速地运行,并且有充分的安全性,此外还不必进行代价昂贵的运行时检查。这是Java重要的创新之一。

校验器是一种数学“定理证明机”。它从Java字节码入手,应用简单的推导规则来确定字节码在某些方面将是如何表现的。较之于此类其他语言的代码,已编译Java字节码中包含有更多的类型信息,因此这种分析是可能的。字节码还必须遵循一些额外的规则以简化其操作。首先,大多数字节码指令都只是在单个的数据类型上操作。例如,对于栈操作,在Java中有关对象引用的指令和有关各种数字类型的指令都是不同的。类似地,将各种类型的值移入或移出某个局部变量时,也有不同的指令。

其次,对于由任何操作得到的对象,其类型总是提前预知的。不存在使用值的字节码操作,也不存在生成多种类型的值作为输出的字节码操作。因此,总是可以通过查看下一条指令及其操作数来了解将得到的值的类型。

由于操作总是产生一个已知的类型,所以通过查看起始状态,则有可能确定将来某一时刻栈中以及局部变量中所有元素的类型。某一时刻所有类型信息的集合称为栈的类型状态(type state),Java在运行应用之前试图对此加以分析。Java对于此时栈和变量元素的实际值一无所知,只是知道它们是何种元素。不过,这些信息对于加强安全规则以及确保对象不会被非法操作已经足够了。

为了使得对栈的类型状态进行分析变得真正可行,对于Java字节码指令如何执行,Java设置了一个额外限制,即到达代码中同一点的所有路径都必须有相同的类型状态。

Java还增加了第2个安全层,即类加载器。类加载器负责将一个或多个Java类的字节码置入到解释器中。每一个要从网络加载类的应用都必须使用类加载器来处理这一任务。

类已得到加载并且通过了校验器检查之后,它将保持与其类加载器的关联。由此,类可以根据其来源而有效地划分到不同的命名空间中。当一个已加载类引用另一个类名时,新类的位置则由原来的类加载器提供。这说明,从一个指定来源获取得到的类,只能与从同一位置得到的其他类进行交互。例如,支持Java的Web浏览器可以使用类加载器为从一个给定URL加载的所有类构建一个单独的空间。基于密码签名的类这样的高级安全性,也可以使用类加载器来实现。

对类的搜索总是从内建Java系统类开始。这些类从Java解释器的类路径(classpath,见第3章)所指定的位置加载。类路径中的类仅由系统加载一次,而且不能被替换。这说明,应用程序不可能使用自己的版本来替换基本系统类以期修改其功能。

最后,安全管理器要负责做出应用级安全决策。安全管理器是一个可以由应用安装的对象,用以限制对系统资源的访问。应用每次试图访问诸如文件系统、网络端口、外部进程以及窗口环境等内容时,都会询问安全管理器;安全管理器可以允许也可以拒绝相应的请求。

对于将不可信代码作为其正常操作一部分来运行的应用来说,安全管理器至关重要。例如,一个支持Java的Web浏览器可以运行applet,而applet可能来自于网络上的不可信的源,这样的浏览器首当其冲就需要安装一个安全管理器。此安全管理器再对此后所允许的访问类型加以限制。这就使得应用在运行任意一段代码前都可以施加一个有效的可信级别。而且一旦安装了安全管理器,它就不能被替换。

安全管理器与一个访问控制器协同工作,通过编辑一个声明性的安全策略文件,从而允许我们从一个较高的层级来实现安全策略。访问策略可能很简单,也可能相当复杂,这取决于特定的应用。有时只需拒绝对所有资源的访问,或者拒绝对于诸如文件系统或网络等通用服务的访问,尽管这种策略很简单,但也已经足够。不过,基于高级信息来制定复杂的决策也是可能的。例如,一个支持Java的Web浏览器可以使用一个访问策略,从而使用户能够指定applet的信任程度,或者允许或拒绝对特定资源的访问,在此可以针对每种不同情况有不同的设置。当然,这里假设浏览器能够确定必须相信哪些applet。稍后将介绍这一问题是如何通过代码标记解决的。

安全管理器的完整性依赖于Java安全模型在更低层次所提供的保护。如果没有校验器和类加载器所提供的保证,有关系统资源安全性的高级断言就没有意义。由Java字节码校验器所提供的安全表明,解释器不能被破坏或搅扰,而且Java代码必须如期使用组件。这反过来说明了类加载器可以保证应用在使用核心Java系统类,而且这些类是访问基本系统资源的唯一途径。适当地施以如上的限制,就可以用一个安全管理器以及用户定义的策略来集中把握对这些资源的控制了。

要有足够的能力来完成一些有用的事情,这与拥有全部能力来完成可能的任何事情之间存在着细微的界限。Java为一个安全环境提供了基础,在此不可信代码可以得到“检疫”、管理和安全地执行。不过,除非你满足于将该代码永远置于一个小小的黑箱中,并且只是独立地运行而不与外界打任何交道,否则就必须对其授权,允许它至少能够访问一些系统资源,这样才能真正有用。每一种访问都既有风险,又有收益。例如,在Web浏览器环境中,授权一个不可信(未知)applet访问你的窗口系统有一个好处,即它可以显示信息,并允许你以一种有用的方式进行交互。与此连带产生的风险则是该applet可能显示一些无意义的东西,甚至是讨厌的或者攻击性的内容。

在一种极端的情况下,运行应用的简单做法是使应用得到一个资源,即计算时间,对此它可能会很好地加以利用,也可能会轻易地将其浪费。不可信的应用会浪费你的时间,甚至会尝试一种拒绝服务攻击(denial of service),这种情况很难避免。而在另一种极端情况下,一个功能强大而且可信的应用应当能够访问各种系统资源(例如,文件系统、进程创建、网络接口等);但恶意应用若拥有这些资源则会带来严重的破坏。这说明,安全性问题相当重要(有时还相当复杂),必须得到解决。

在某些情况下,只是要求用户对请求做出“同意”与否的回答,这是一种可以接受的做法。Java语言提供了工具来实施你想要的任何安全策略。然而,这些策略将会是什么样,最终取决于相关代码的身份认证和完整性有多么敏感。这是数字签名的用武之地。

数字签名以及证书等技术都是用以验证数据确实来自于它所声称的来源,而且在发送过程中未经篡改。如果Boofa银行对其支票簿applet做了签名,你可以验证此签名,从而确定该applet确实来自于该银行而非假冒,而且也未被修改过。因此,你可以通知浏览器信任有Boofa银行签名的applet。

由于一切都尚在进行之中,因此很难将目前有什么、将来可能有什么,以及哪些已经存在一段时间等诸多情况呈现于笔端。以下几节将勾勒一幅路线图,从而使你对Java的过去、现在和将来有所认识。

Java 1.0为Java开发提供了基本框架,即语言本身以及允许编写applet和简单应用的包。尽管1.0已经正式废弃,但仍存在大量applet遵循其API。

Java 1.1取代了1.0,主要在以下方面有所改进:AWT包(Java最初的GUI功能)、一个新的事件模式、诸如反射和内部类等新的语言功能,以及许多其他关键特性。Java 1.1是Netscape Navigator和Microsoft Internet Explorer浏览器的大多数版本多年来都支持的版本。出于一些政治上的原因,浏览器领域可能还会在这种状态下停顿较长一段时间;Java 1.1这个版本仍然会被认为是applet的一种基础,尽管它会随着Microsoft在其平台中放弃对Java的支持而消失。

Java 1.2(即Sun所称的“Java 2”)是一个于1998年12月发布的主要版本。它提供了许多改进,并增加了大量内容,主要是将API集捆绑至标准发布版来实现的。所增加内容中,声名最为显赫的当属将Swing GUI包作为核心API,并且加入了全新的成熟2D绘制API。Swing是Java的高级用户界面工具包,其功能远远超出了原来的AWT(Swing、AWT和其他一些包有时被称为JFC,即Java基类(JavaFoundation Class))。Java 1.2还为Java增加了一个适当的集合(Collection)API。

Java 1.3于2000年初发布,其中仅增加了少许特性,但是将重点在性能上。利用1.3版本,在许多平台上,Java的速度都得到了显著提高,而且也修补了Swing的许多bug。在这一阶段,诸如Servlet和Enterprise JavaBeans(EJB)等Java企业API也日渐成熟。

Java 1.4于2002年发布,加入了一个重要的新的API集,以及很多人们期待已久的特性。这包括:语言断言、正则表达式、首选项和日志API、一个面向高容量应用的新的I/O系统、对XML的标准支持、AWT和Swing中的基本改进,以及针对Web应用的相当成熟的Java Servlets API。

Java 5发布于2004年。这是一次重要的发布,引入了很多期待已久的语言语法扩展,包括范型、类型安全、枚举、增强的for循环、可变的参数列表、静态导入、基本类型的自动装箱和拆箱,以及关于类的高级元数据。一个新的并发API提供了强大的线程功能,还有类似于C中所添加的那些用于格式化打印和解析的API。RMI也进行了彻底的修改,以不再需要编辑stub和skeleton。标准XML API中也有重要的增加。

Java 6于2006年发布,是一个相对较小的发布,没有给Java语言添加什么新的语法特性,但是,绑定了一些新的API,例如用于XML和Web Service的那些API。

本书包含了Java 7这一最新版本中的所有最新和最好的改进。这个发布版本添加了少许的语言功能的增强,例如,改进了异常处理和资源管理。它还包含了一些重要的API更新,例如,一个完整的新的文件系统API,还添加了很多其他的API。本书的这一版本是Java 5之后的第一次改版,因此,彻底地进行了修改,包含了Java 6和Java 7版本中进行的所有修改。

以下对当前核心Java API的最为重要的特性提供一个概述:

JDBCJava Database ConnectivityJava数据库互连)

为了与数据库交互的一个通用功能(最早于Java 1.1中引入)。

RMIRemote Method Invocation,远程方法调用)。

Java的分布式对象系统。RMI允许调用某个服务器中的对象上的方法,而该服务器可能运行于网络上的另一位置(最早于Java 1.1中引入)。

Java SecurityJava安全性)

用于对访问系统资源加以控制的功能,并结合有一个统一的加密接口。Java安全性是签名类的基础,对此先前曾讨论过。

JFCJava Foundation ClassesJava基类)

包含大量新特性的完备集(catch-all),其中包括Swing用户界面组件;“可插拔感观”(pluggable look-and-feel),这表示用户界面能够自行调整以适应所用平台的观感;拖曳功能;以及可访问性(这意味着集成特定软件和硬件的能力以为残障人士服务)。

Java 2D

JFC的一部分,支持高质量图形、字体管理和打印。

Internationalization(国际化)

编写程序从而使之能够与用户希望使用的语言相适应的能力,程序可以自动地以适当的语言显示文本(最早于Java 1.1中引入)

JNDIJava Naming and Directory InterfaceJava命名与目录接口)

查找资源的一个通用服务。JNDI统一了对诸如LDAP、Novell的NDS以及其他目录服务的访问。

以下“标准扩展”API并非核心Java发布版本中的一部分,可以单独下载。

JavaMail

编写E-mail软件的一个统一API。

Java 3D

此功能可以用于开发带有3D图形的应用。

Java Media

另一个完备集,其中包括Java 2D、Java 3D和Java媒体框架(Java MediaFramework,这是一种用于调整不同类型媒体显示的框架)、Java Speech(用于语音识别和合成)、Java Sound(高质量音频)、Java TV(用于交互式电视和同类应用)以及其他。

Java Servlets

允许用Java编写服务器端Web应用的功能。

Java Cryptography

密码算法的实际实现(出于法律的原因,这个包与Java Security是分离的)。

JavaHelp

编写帮助系统并将其结合到Java程序中的功能。

Enterprise JavaBeans

用于构建分布式服务器端应用的体系结构。

Jini

一个非常有趣的完备集,其设计目标是支持大范围分布式计算,包括查找各种设备并与之交互,这些设备从软件工具到硬件,甚至家用电器。

XML/XSL

一个工具,用来创建和操作XML文档、验证文档、将其与Java对象相互映射,并使用样式表传输XML。

Web services

创建并部署基于Java的SOAP Web Services的工具。

在本书中,我们将尽可能使你了解到更多的特性;对我们而言,遗憾的是(对于Java软件开发人员倒是一件幸事),Java环境变得涵盖面如此之广,以至于我们无法仅在一本书中做到面面俱到。

Java的变化已经不那么频繁了,因为多年来Java已经日渐成熟,但是,Java仍然是应用开发的最流行的平台之一。在Web Service、Web应用程序框架和XML工具等领域,尤其是这样。尽管Java还没有像预想的那样统治移动平台,但是,Java语言及其核心API已经用来为Google的Android移动OS编程,该系统在全世界数以百万计的设备上使用。在Microsoft阵营中,由Java衍生而来的C#语言占据了大多数的.NET开发,并且将核心Java语法和模式带到了那些平台中。

可能Java如今的改变中,最激动人心的领域在于这样的一种趋势:朝着轻量级、简单的商用框架发展,并且Java平台集成了动态语言以支持Web页面脚本编程和扩展。这将是非常有趣的工作。

对于Java开发环境和运行时系统,你有诸多选择。Oracle的Java软件开发包可用于Mac OS X、Windows和Linux。

访问Oracle的Java站点,以了解获取最新的JDK的更多信息。本书的网上内容,则可以通过http://oreil.ly/Java_4E获取。

还有很多流行的Java集成开发环境(Integrated Development Environments,IDE)。本书中,我们将介绍两种,分别是IBM的Eclipse和Oracle的NetBeans IDE。这些一体化的开发环境允许我们轻松地使用高级工具来编写、测试和打包软件。尽管Eclipse毫无疑问是最流行的,并且是开源的,但本书作者喜欢的IDE是JetBrains的Intellij IDEA,它现在也有一个免费的社区版。

 例如,参见G. Phipps, “Comparing Observed Bug and Productivity Rates for Java and C++,”Software—Practice & Experience, volume 29, 1999

 汽车的类比源自于Marshall P. Cline,他是C++ FAQ一书的作者。


相关图书

Effective Java中文版(原书第3版)
Effective Java中文版(原书第3版)
Java核心技术速学版(第3版)
Java核心技术速学版(第3版)
Java编程动手学
Java编程动手学
Java研发自测入门与进阶
Java研发自测入门与进阶
Java开发坑点解析:从根因分析到最佳实践
Java开发坑点解析:从根因分析到最佳实践
Java EE企业级应用开发实战(Spring Boot+Vue+Element)
Java EE企业级应用开发实战(Spring Boot+Vue+Element)

相关文章

相关课程