Java 的可移植性

Java 的可移植性

Michael Edwards
微软公司开发技术工程师

序言<o:p></o:p>

我一直都怀疑被那些自以为博学的人不停讨论的最热门的话题Java™ 。您可以用一分为二的观点来对待这些问题,最终你被迫相信 Java 将带来世界和平并能偿还美国的国债或者它完全是在浪费时间。我常常想涉足这些讨论,但我还是尽量避而不谈,直到我能够提供广博的观点。<o:p></o:p>

设想一下当我从圣诞节假期归来时发现我的下一个任务是需要学习 Java 时,我是多么的惊慌(或者应当说成是恐惧)。你能说“留心你想要得到的东西,因为你可能正在得到它”吗?但是,我很高兴的发现学习比我预计的要容易。Java 是面向对象的设计方法,作为一个长期在复杂凌乱的 C++ 世界挣扎的 C 程序员,经过多年的面向对象的编程,作为一种回报我发现确实学到了一些原理和技术。的确,我承认我是一个 OLE 热衷者(阅读过 Nigel Tompson 的系列文章的 MSDN 订阅者都会理解我的意思),至少在产品代码迁移方面如此。但是,渐渐的我离开了无序的行列,至少在考虑 Java 时是这样。<o:p></o:p>

我的第一个决定——并且是一个正确的决定——避免了选择类似于《利用你五天刷牙的时间学会 Java》这样的书。如果你最近到过书店的计算机技术部分(如果你可以外出),会知道那里有许多 Java 书籍。为了缩小你的查找范围,你可以选择适合自己的书。因为我有丰富的 C 程序经验,我找到一些适用于 C 程序员学习 Java 的书。例如,找到一本David Flanagan 所著的《简单的Java》(Java in a Nutshell 专用系统参考 1996年12月。ISBN:1556592183)。我还买了一本Addison-Wesley 出的《Java 系列》(参见http://www.awl.com/cp/javaseries/),这些书的确十分有用,但大部分都是编程参考。按照我自己通过实践进行学习的习惯,没过多久我就熟悉了。我开始时编写示例 Applet 程序,这在当时看来是很有用的,但很快我就认识到自己的知识很有限需要深入的学习。于是我开始浏览微软的Java 开发者 Web 站点和Sun MicroSystems 站点, 并且在下列站点也找到一些资料(这不是一个全面的列表):<o:p></o:p>

·        Java 开发者 (最好的Java 编程疑问与解答) <o:p></o:p>

·        “Java Jolt”  (Web Developer.com 的指导专栏周刊) <o:p></o:p>

·        “Digital Espresso”  (Java 邮件列表摘要周刊和Mentor Software Solutions 的新闻组活动) <o:p></o:p>

·        JavaWorld  (每月更新的关于 Java 社团的IDG 杂志) <o:p></o:p>

·        Javology  (每月更新的 Java 新闻和在线见解e-zine) <o:p></o:p>

权威人士一些关于 Java 的观点让我感到鼓舞,我又回到原先起步阶段的 Applet 示例中,但是很快就又转移了视线,开始阅读 Sun Java 开发工具(JDK)的 Applet 示例源程序——可以从http://www.javasoft.com/nav/developer/index.html 下载。自然,当我开始看实现系统类的源代码时,觉得阅读示例源程序更有趣。<o:p></o:p>

我的 Java 学习方法实践证明是比较成功的。由于正确的选择了适合自己 C 编程背景的启蒙书籍,我对 Java 有了相当充分深入的理解。学习的第二阶段通过到 Web 站点浏览,我又掌握了关于 Java 开发工具和相关问题的最新信息。经过阅读源代码,使我对 Java 的结构和实现有了本质的认识。<o:p></o:p>

关于 Java 系列文章的第一篇将帮助每个 C 程序员解答“Java 可移植性的关键是什么?”这个问题。我将重点介绍设计方面使 Java 应用程序比其他技术在不同计算机平台之间有更好的可移植性的原因。但是,如果你想彻底理解这一切,Java 良好的可移植性内部的理论将是你学习的一个重点。我看到在 Java 新闻组中有大量的布告,反映出实际使用时不断出现的真正的问题。当然,人们也在抱怨 Java 的兼容性问题,我将从根本上解释这一问题。如果你认为该主题的文章有帮助,我将很感激你能告诉我(我的 e-mail 地址是[email protected] )。<o:p></o:p>

关于可移植性<o:p></o:p>

提供创建高可移植性应用程序的便利是 Java 设计体系的核心要素。我的大部分编程经验是关于 C 语言的,八十年代当我开始自己的计算机游戏程序员生涯时,我就开始与可移植代码问题朝夕相伴。我所在的游戏公司常期支持几个不同的平台,包括Apple II、C64、Mac、Amiga、Atari ST 和 PC——使用游戏控制台,但未能成功。因此,“移植仪定”仿佛成了我的名字。当我在这些平台之间移植游戏时我学到了重要的一课:不存在完全可移植的代码;仅仅是可移植代码多和少的问题。我同时也掌握了一个可以产生尽可能多的可移植代码的方法。对我们来讲,这个方法包括尽可能多的用 C 语言编写代码,使用我们称为虚拟机的技术——我们移植到不同平台时所使用的基本库代码,该库充当运行我们的游戏程序所需的虚拟图形和用户输入库。因此,从我个人来之不易的创建可移植软件经验,我能证实 Java 通常使用可靠性好的技术来生成高可移植性的代码更容易。<o:p></o:p>

总体上讲,我认为虚拟机方法将所有不同的平台平等对待来自一定的协定。为了解释这一点,让我讲述一个关于我所作的一个工程的故事,这是在我退出开发计算机游戏之前的最后一个工程。1988年,我所在的游戏公司面临一个艰难的决定,是否放下 Amiga 和Atari ST 对我们 PC 游戏的支持。那时我年轻而又天真,这个问题激怒了我。我们既然已经在 Amiga 上取得了初步的成功——我们怎么能考虑要放弃所创建的最好的游戏计算机?于是我日夜不停的工作了三个月来将我们的虚拟机移植到Amiga 和 Atari ST 平台上,并且我深信我可以将发布这些版本的问题化简为简单的重新编译问题。自然,我成功的实现了我们虚拟机的移植,甚至我还用了不到一周的时间演示了一个我们游戏的Amiga 和 Atari ST 版本。你能猜想到发生了什么吗?最终我们还是放弃了该平台。在1988年拥有 Amiga 的人中没有愿意仅仅为了 PC 游戏的移植而花钱的。如果他们想拥有一个 PC,他们会买一台 PC。相反,如果他们想要一个真正的游戏计算机,他们会买一台Amiga(这毕竟是1988年)。从中我接受了一个沉重的教训:虚拟机迫使你在编写软件时要在所需支持的结构上采用尽可能少的非公共标准。如实地支持不同结构中的不同特性需要额外的工作而且并不一定总是一个可行的选择。<o:p></o:p>

一个简要的澄清:我的同事程序员Paul Johns 审阅这篇文章时,他指出我的不恰当修辞比喻暗示:可移植代码与跨平台代码相同。可移植性意味着简单的重新编译一下软件就可以运行在另一个平台上,而跨平台(Java 辅助建立的定义)意味着只要编译一次代码就能够运行在任何地方(或者按照批评者所说的,编译一次就可以运行在任何它可以运行的地方)。我认为Paul 是正确的跨平台代码与可移植代码不同,但是你可以认为跨平台代码是所有可移植代码的根源。<o:p></o:p>

是什么使得 Java 适合各种需求,使我们称它为跨平台可移植的呢?有三个主要的原因:结构上提供了源代码级的可移植性;应用程序编程模型被广泛而精确的描述(特别是作为语言的一个部分本身);语言的几个要素使它们易于移植。<o:p></o:p>

虚拟机<o:p></o:p>

我已经反复读到Java 是一个解释型编程语言。但是,这并不是严格正确的;更准确的讲 Java 语言通常(不总是)编译成字节代码,而字节代码有时(但不总是)被解释。因此, Java 源代码并不直接被翻译为特定计算机所特有的低级机器指令。但是等一下——当我修补 Java Applet 示例程序时,我用一个编译器从它的源代码产生一个 Java 类文件。当我用Microsoft Internet 浏览器测试编译好的Applet 时,我使用Microsoft´s Just-In-Time 编译器来加速Java Applet 的执行(参见 MSDN 库中的“Just-In-Time 编译器的描述”,基本知识文章 Q154580)。<o:p></o:p>

什么是被编译,什么是被解释?Java 编译器产生的二进制指令(被称为字节代码)是由一组崭新的虚拟机本身的低级机器指令组成。用于解释已编译Java 字节代码的虚拟机作为被创建的一个软件机器本质上讲可以运行在任何计算机上——甚至可以被创建为一个新的硬件平台,它使用Java 字节代码作为它自身的机器指令。换句话说,Java 虚拟机可以用硬件或用软件实现,而Java 程序并不“知道”其中的区别。事实上,如果Sun Microsystems JavaStation(一种被称为网络计算机的想法)直接执行Java 字节代码(我不能肯定的说),我们可以认为Java 程序是在由软件实现的 Java 虚拟机上仿真。在一个完全不同的计算机上仿真为另一个计算机结构编写的程序这一概念当然并不新鲜。例如,一些十分热爱他们旧的8位游戏的人们费尽心机的使自己可以在 PC 上继续玩。但Java 精确定义的语言规格和虚拟机目的在于使其可以运行在不同的结构上。这成了Java 被那些关心跨平台移植性的人们感兴趣的核心因素——它提供了一个精确定义的语言和虚拟机规格,使在任何计算机平台上创建虚拟Java 计算机成为可能。<o:p></o:p>

这在理论上讲的确很好,但对此文持怀疑态度的批评家却更关心在不同的代码基础和不同的平台上提供相同的行为所面临的巨大挑战。事实上,大量的相当新的 Java 实现正在被公开,因此我们可以开始收集新理论变成实际的成功信息。如果我不得不决定是否推荐转向 Java 利用它的跨平台特性,我将仔细的研究 Java 实际的兼容性优点。(正象Ronald Reagan 所说的)至少,我将在我打算运行软件的每个地方测试它,对我发现的问题我将提出自己的看法。“吱吱响的轮子会得到加油,不响的轮子却会坏掉。”<o:p></o:p>

如果你读过基础知识文章Q154580,“Just-In-Time 编译器描述”(MSDN 基础知识库),你会看到代码在虚拟机上被执行之前通过将 Java 字节代码编译为不同的本地代码可以改变 Java 的运行特点。因此,取代解释该程序,你可以正常的执行它。这给了你在源代码级上的移植性,使你可以保留程序的优点,在首选位置为自己的计算机编译高效的代码。<o:p></o:p>

从自称对可移植性无所不知的人们中我听说一个正在酝酿的强烈的意见:C 语言狂热者声称通过重新编译 C 语言也可以移植,Java 的狂热者却说重新编译是移植性问题的一个可悲的借口。我塞上耳朵不理会这些——一旦你理解了 Java 语言的解释方面的特性(这使得对跨平台的支持到了源代码级)正是 Java 可移植性的重要部分,那些说法几乎是毫不相关的。<o:p></o:p>

并不是另一个 API<o:p></o:p>

解释型的语言本身并不意味着它的程序自然可以有跨平台的兼容性,因为程序中的所有因素取决于其下的操作系统。世界目前还没有一个公用的方法来从不同的操作系统中获取这些要素(如果你不要求 Java 这样做)。因此,除了掌握他们首选的编程语言外,软件开发者坚持学习不同的应用程序编程接口,或者 API。即使你对 C 语言熟悉的就象自己的手背一样,要开发各种各样平台上的 C 应用程序意味着你必须也要成为一个各种 API 的专家。事实上,目标平台上提供的各种 API 之间的区别是设计可移植程序的关键。Sun 通过为他们定义的“Java兼容”的平台描述一个标准的 Java API 来解决可移植这个复杂的问题。但是你知道什么呢?对于任何支持跨平台 Java API 这个概念都十分有效。将语言的概念与操作系统特定的 API 相合并都象在说:“如果你打算用 C 语言编程,你必须使用Win32® API”。冒着可能受到限制的危险,将语言的概念与 API 组合成一个单一的包的确是面向创建可移植软件迈出的一大步。瞧!我现在理解了 Java 的真实含义。它不只是一个语言,或仅仅是一个 API,而是两者的结合!<o:p></o:p>

将语言和 API 合成一个单一的源,有一个潜在的缺点:如果你不喜欢 API,或所需的部分没有发布,或者你发现其中的许多问题等等,你将无能为力。但是,Java API 可以被扩展,实际上 Sun 的 Java 小组正努力解决在已发布的两个版本中诸如过分简单的图形和多媒体支持此类的问题(参见http://java.sun.com:81/products/api-overview/index.html)。当然,当不能通过对现存 Java 虚拟机顶层类派生的方法实现所需功能时,可以扩展 Java API,同时又引入了新的移植性问题。如果新的 VM 在所有 Java 兼容的平台实现这些扩展之前发布,作为扩展加入标准 Java VM 的新内容将导致新的问题。原先下载新 VM 的烦恼将会刺激人们,尤其是如果他们的 VM 是浏览器固有的一个部分时,他们将不得不连同浏览器一同升级。另一方面,如果对 VM 的扩展不能在所有的平台上实现时,将带来问题。<o:p></o:p>

由于 Java 是被解释的,在 Java 中创建新类库是另一个扩展跨平台 API 的方法。这也就是说,通过使用 Java 语言本身你就能以一种完全可移植和兼容的方式进行功能扩展。当然,如果你不能解决关键性问题或下层 VM 的不足,你可能被限制去做你想做的事情。但是你的 Java 程序将是可移植的。事实上,许多公司正在发布带有全部跨平台和扩展功能特性的 Java 类库——例如,微软新的应用程序基本类(AFC)。关于 AFC 的更多的信息,参见http://www.microsoft.com/java/default.htm。<o:p></o:p>

在 JDK 1.0.2 和 1.1 版中,22个 Java 用户接口类描述了一套必须使用本地机操作系统实现的对象。为了实现这些,Java 将这特殊的22个类中的每个类与一个对等对象联系。对等对象负责绘制该对象和处理用户输入的事件。这22个类是高级窗口工具包的一部分并在 Java 和 Java VM 组件的本地代码之间提供了一个明确定义的交互作用,同平台特有的外观和感觉一样(参见 Sun Java 指南站点上的“组件结构的细节”)。一个对等对象用本地方法实现它的大部分功能。如果在基于Windows® 机器上你用调试器跟踪自己的 Java 代码到一个本地方法调用,并单步执行到本地代码,另一端将出现在 Windows 动态链接库(DLL)中。如果你象我一样总是喜欢知道事物是如何工作的,你可以查看微软的 Java SDK 站点来查找微软的 Java VM 的本地方法是如何作用的(在左边的帧中单击“Raw Native Interface”)。我仍旧叙述平台特定的外观和感觉部分是如何工作的(和它是如何改变的),因为我认为在跨平台移植中这是关键性的一章。<o:p></o:p>

在容器中布局用户界面组件也有潜在的移植性问题(参见 Java 指南的布局部分),因为 Java 通过一个允许插入定制管理器的布局管理器体系结构放置组件到指定的位置。为了确保可移植性,包含其它对象的对象不能过分注重所包含对象实际所处的位置。否则,容器代码中需要处理象屏幕分辨率、容器尺寸、视图属性和其它无关的细节。通过将 Java 容器对象和布局管理器对象联系,你可以一次性解决布局问题。你可以通过定制布局管理器在容器中创建各种视图(参见 Java 指南http://www.javasoft.com/nav/read/Tutorial/ui/layout/custom.html)。例如,能够插入自己的布局管理器用 AFC 替代 Sun 的实现方法,创造性的将ActiveMovie™ 内容放入ListView 容器。因此,如果你注重自己的 Web 页面在不同屏幕分辨率下的外观,或要在容器中放入新组件,Java 的布局管理器接口可以是一个解决方法。<o:p></o:p>

移植性更好的语言<o:p></o:p>

总之,Java 语言本身的要素使它十分方便的就可以创建跨平台的应用程序。这并不意味着你不能通过其它方式实现可移植性,例如,通过使用带有类似 GNU C 这样完善的引用库的 C 语言。正如我已经陈述的那样,创建可移植的软件主要是有一个合适的程序的问题。然而,从可移植性的观点看 Java 语言已经为此作了许多工作。<o:p></o:p>

例如,象我前面提到的那样,Java 的缔造者努力使语言的任何方面都不依靠 Java 的实现来决定。例如,取整型变量的内存尺寸。在 Java 中,short型总是16位,long型总是32位。变量尺寸严格的定义比 C 语言更有限制性,C 语言中整型的尺寸可以随结构增长,但它比仅仅确保 sizeof(short) <= sizeof(int) <= sizeof(long)有更好的可移植性。类似的,Java 的浮点型变量的尺寸也是固定的(所有浮点运算遵守一个标准 IEEE 754。参见“计算机体系结构原理”的附录 A)。实际上,Java 语言规范中精确定义了在 ANSI C 中留给独立实现的所有基本数据类型的尺寸。<o:p></o:p>

扩展精确定义数据类型尺寸的概念,Java 语言固定使用专有地Unicode version 2.0 字符集。如果你曾经为在支持不同字符集的平台之间复制一些代码而高兴,那么你会认为固定字符集是多么令人愤怒。当然,你可以使用 Unicode 字符集的 ANSI 代码页,但是你的字符将不在是8位的,因此你不会看到任何 Java 源的在 #ifdef 多字符集上出问题。由于这个原因,你不会看到任何 #ifdef,因为 Java 没有预处理。Sun 的 Java 小组指出你并不需要它,因为 Java 提供了更好的方法来实现等价于 #define语句和条件编译的功能,而条件编译通常用于处理不同的依赖于平台的代码的路径选择。目前为止我还没有发现这是一个问题,但是我认为我还没有完全信服。<o:p></o:p>

在 API 中你经常见到的许多东西在 Java 语言中都被直接取代,许多在跨平台代码中不可移植的 API 元素在 Java 中都成为可移植的。例如,同步就包含在其中。如果你有 Win32 背景,你很熟悉同步函数对于保护代码中命名部分的作用。在 Java 中,同步被指定在对象级。这意味着你的应用程序的设计满足在一组自然发生的可重复使用的对象中同步的需求。当然,没有理由不让你建立特殊的 Java 类,就象在“干脆的” Win32 代码中临界区的用法一样使用该类。<o:p></o:p>

线程是经常在 API 中见到的另一个领域,但是已经被直接建立到 Java 语言中。在 Java 中,确定你的线程模型与从自己的应用程序中提取它包含的任务或对象紧密相关。与同步一样,Java 将串行化建立在对象级锁定中,Java 的方法将你的线程活动放在一个对象级别上。与我的传统观念相符,我将 Java 的功能与 Win32 的功能相比较,发现 Java 的不足。例如,用等待函数来同步对象(在 Java 中这是唯一实现多线程执行同步的方式),而这仅仅是 Win32 中控制线程方式中的一种。Java 仅仅提供了单一对象的调度方法,不能在多个阻塞的对象中指定要调度的线程,而 Win32 提供了多对象的调度和预警等待函数(在 MSDN 库中的 SDK 平台中查找“Wait Functions”)。如果你正在处理 Java 的限制,你最好确保你的程序不会与将来的 Java 版本冲突。<o:p></o:p>

当今软件最大的可用性问题之一并不是粗糙的用户界面设计。而是比那简单的多的问题——软件的小缺点。Java 采取一个实际的方法来解决那些普遍存在而又无法预料的程序错误;通过将选中的异常作为语言整体的一个部分和在 JDK 的类实现中抛出适当的异常,强迫开发者考虑当他们的程序失败时如何处理。在编译时,选中的异常允许编译器校验是否有一段代码来处理执行该函数时可能发生的问题。换句话说,除非你正确的处理了自己的错误,否则你不会通过编译检查,实现单独运行。多么好的概念啊!可什么时候才是你最后一次为无法解释的软件错误痛苦的时候呢?以我的经验,创建健壮的软件需要在每行代码编写前都要有专业的协调努力,我很欣赏 Java 所提供的额外的标准来确保确实进行了这种努力。当然,一些 C++ 编译器(包括微软的)有异常检查,但是这个特性不是可以与 API 合为一体的标准的语言要素。编译时的错误检查提供了另一个将语言的概念与有优点的 API 合并的例子。(在 Web 上,我遇到这样一篇文章“将一个 C++ 通信软件转变为 Java 框架的经验”如果你对此感兴趣可以去看看)<o:p></o:p>

你是否真的关心可移植软件?<o:p></o:p>

当我自学 Java 时创建的 Applet 是我曾经编写过的不需任何“额外”工作(除了测试,当然也有一些小毛病需要我改正)就可以在多个平台上运行的程序。另一方面,我已经与许多关心可以在任何地方运行的软件开发者谈论过这些,但还没有被他们所信服。毕竟,Windows 和 MS-DOS® 占据着我们时代的统治地位。但是核心平台的观点忽略了在 Web 上只有内容才是核心的事实,你需要每个人都能够访问你的内容不论是现在还是将来。<o:p></o:p>

即使你不关心可移植性,也会有其他人去做。例如,在企业中,在所有大型机、服务器、台式机、便携式和手持式之间的自动兼容可以极大的减少编程量和技术支持。当然,Web 出版物可以到达各种 Unix 工作站和 Windows CE 手持式 PCs。<o:p></o:p>

我曾经与一些认为 Java 是被过分宣传的、将很快成为失败的被忘记的技术的人们讨论过。另一些人们相信对 Java 的将来如此暗淡的估价是生活在过去的人们的自嘲。我认为事实可能是在两者之间中间结果:Java 是一个工具,象其他工具一样,只有被正确的使用才能体现它的价值。<o:p></o:p>

如果你觉得这篇文章对你有帮助,或你有什么建议、苦恼、问候,请发 EMAIL 给 [email protected]让我了解。 

你可能感兴趣的:(java,游戏,编程,虚拟机,软件测试)