最近我们收到一封电子邮件,咨询 “什么是Java?”条目的信息。在2006年,难道还有人不知道“什么是Java”吗?十年来,有大量介绍Java的书籍、网站和会议,难道不是所有人都知道“什么是Java”吗?显然答案是否定的。
毕竟,情况已经改变。
每个涉及applet和实时(Just-in-time)编译器的含糊定义都有许多已固定下来并为许多人所了解的新说明和新事实,但它们并非全部都写入文档中了。过去,Java常常意味着:
•Applet
•字节码解释
•缓慢的性能
•等待Sun恩赐的“拜物教”
而如今,它意味着:
•Web应用程序、Web服务、SOA等等
•热点动态编译
•高性能
•一个日益独立于Sun的开源社区
过去有个口号叫做“一次编写,随处运行”,这仍然是事实,但编写的内容及其运行环境和方式正在改变。
Java编程语言
Java是一种面向对象的高级编程语言,它在许多方面受到C、C++和Smalltalk的影响,还借用了其他语言的概念。其语法的设计方式使得那些熟悉“大括号”语言(继承自C)的人也会熟悉Java语法,但它具有比C++更强的面向对象性、对象的静态类型转换以及相当严格的异常系统,该系统要求调用堆栈中的每个方法要么处理异常,要么声明其抛出异常的能力。当然还有垃圾自动收集功能,这使开发人员不必释放由废弃对象占用的内存。
Java的一个比较受争议的方面(这些方面在发布Java时被广为接受,但现在正日益受到批评)是它的不完全的面向对象性。具体来说,Java基本类型(如int、char、boolean等等)都不是对象,并且开发人员需要以完全不同的方式来处理它们:由于int不是类,因此不能为其创建子类并为其声明新方法,也不能将它传递给需要普通对象的方法,诸如此类。基本类型提高了Java的性能,但却降低了代码的清晰度,这一点使用所谓的“包装器类”(Integer、Character和Boolean)的人应该深有体会。Java 5.0引入了autoboxing(自动装箱)模式,以消除许多使用包装器类的用例,但在某些方面这使代码的功能不那么明显了。
从理论上讲,Java是种“早期出错”语言。由于它的语法约束,许多编程错误在Java中不可能出现。由于不能直接访问指针,所以指针运算错误也就不存在了。使用对象时的类型如果与当初声明它的类型不同,就会要求进行显式的类型转换,这使编译器能够拒绝不合逻辑的编程,如对一幅图像调用一个字符串方法。
许多Java企业框架都要求使用配置文件或者部署描述符(通常用XML编写)来指定操作:哪个类处理特定的HTTP请求、在规则引擎中执行的步骤顺序等等。实际上,要实现这些功能不能只用这种语言。评论人士指出,这会产生不当后果:不仅避开了Java编译器的检查,而且开发人员无法再(只)根据程序的源代码就可确定它如何运行。Java 5.0为该语言添加了注释(annotation)特性,它允许使用值为方法、字段和类添加标签,在运行时,通常可通过反射对这些值进行内省和操作。许多程序员喜欢注释,因为它简化了工作,否则就需要通过部署描述符或其他方法来解决问题。但是,注释也有可能使Java代码难以理解,因为注释的有无可能会影响代码的执行方式,而这从注释中不太容易看出来。
尽管存在这么多的批评意见,但Java通常还是被认为是当今最流行的通用计算语言。在企业编程领域,它是一个广泛使用的标准,而且2005年它取代C++成为SourceForge项目使用最多的语言。使用Java有很多好处:免费的工具(适用于多种平台:Linux、Windows、Solaris和Mac均可编译和执行Java应用程序)、内容丰富的知识库以及大量乐意提供帮助的开发人员。
Java语言已经达到了开发人员生产率与代码性能之间的一个特定平衡点:CPU周期成本持续降低,但开发人员的开发周期却并未明显缩短,因此在开发人员与CPU操作码执行之间再出现一个抽象层也许是不可避免的了,它将使开发人员能够更快地创建更好的软件。实际上,Java生产率的批评者(如《Beyond Java》的Bruce Tate)可能正是观察到了这种不断推进Java使其达到新的平衡点,从而进一步牺牲性能去换取更高的开发人员生产率的趋势。
Java平台
通常有三种Java平台:Standard Edition(标准版,SE)、Enterprise Edition(企业版,EE)和Micro Edition(微型版)。每个平台都是一个包含某个语言版本、一组标准库和执行代码的虚拟机(见下文)的组合。EE是SE的超集,任何EE应用程序都可假定所有的SE库都存在。EE平台的语言使用与SE的一样。
由于小型设备(如:电话或机顶盒)的局限性,Java Micro Edition与另两个版本有很大区别。它并非SE的子集(像SE是EE的子集那样),因为它的一些库只存在于Micro Edition中。而且,ME取消了一些语言特性,如float类型和Float类,这反映了它的运行平台的局限性。ME需要与SE和EE不同的工具,而且设备之间的巨大差异使ME领域代码的可移植性更加不现实,因此许多Java开发人员将ME视为异类。
Java虚拟机
在某种程度上,Java源代码需要成为平台自带的可执行代码。这个过程一般需要两个步骤:开发人员将源代码编译成Java字节码,然后Java虚拟机(JVM)将其转换为主机平台的本地代码。第二步最初是通过解释方式执行的:读取每条JVM指令,然后动态地将其转换为一条或多条本地指令。然后,在程序开始运行时,实时(just-in-time,JIT)编译器将所有的Java程序从JVM字节码转换为本地代码。如今,该过程有多种实现方式。Sun的HotSpot编译器在运行时解释并分析代码,编译并优化对程序的操作最为关键的那部分。IBM的JVM工作原理与此非常类似。这些方法避免了由于对整个程序进行实时编译所导致的启动时性能下降,随着时间的推移,性能将会恢复,因为关键的代码部分已被定位并优化。长时间运行的服务器进程很适合采用这种方法,但这对客户机应用程序不太适用。
就像基本类型一样,现在批评人士认为Java的这个两步编译周期是一种不成熟的优化方法。他们提出疑问:如果要等到运行时将Java字节码编译为本地代码,那么为何不采用解释Java源代码(而非Java字节码)的方式,从而为开发人员节省一个步骤?正如Tate在《Beyond Java》一书中所说的那样,“Java并不是最简单的语言。它对很短的迭代也不友好……其他语言允许轻松地应用更改,而无需麻烦的编译/部署周期。”
没有Java的JVM
实际上,Tate在寻找秉承Java成功表现的后继者的过程中抱有这样的理念:“下一个在商业上取得成功的语言应该拥有在JVM上运行的版本。这将有助于该语言克服许多障碍,不管是在策略上还是在技术上。”他指出,虚拟机方法可提供安全性(“如果能确保虚拟机的安全性,则要确保语言的安全性就容易得多了”)、可移植性、互操作性和可扩展性。由于JVM已有效地解决了这些问题,因此如果新语言可运行在已安装于数百万台计算机中的JVM上,那么它就不需要自己的虚拟机。
在许多方面,这种情况业已发生。用Java为脚本语言编写解释器可有效地将这些语言移植到JVM上,如:用于JavaScript的Rhino、用于Python的Jython或者用于Ruby的JRuby。
但也可以完全绕过Java语言,而直接进入JVM级别。已经有一些将C转换为JVM字节码的编译器,如商业工具Axiomatic Multi-Platform C,它提供了ANSI C的子集。而且,Java字节码处理工具(如ASM和Apache BCEL)的发展允许Java应用程序在运行时创建可执行的类。这些类不再是Java语言,而是一种用于JVM编程的有效汇编语言。
或许由于意识到了在JVM上运行非Java代码的需求,最近已提交了一项新的JSR(Java规范请求),即“Supporting Dynamically Typed Languages on the Java Platform(在Java平台上支持动态类型化语言)”(JSR 292),它指定了一种新的字节码,将使JVM更适用于运行不含静态类型信息的语言。
没有JVM的Java
也可以从另一个角度来看问题,即不使用JVM而运行Java。毕竟从某种意义上讲,Java源代码转换为字节码,反过来字节码又转换为本地代码,没人会说这些转换不能一次完成。GNU Compiler for Java (GCJ)允许一次性地将Java源代码编译为一个平台的可执行代码。不过它尚不完善,不支持Abstract Windowing Toolkit(抽象窗口操作工具包,AWT),因此它不适用于AWT或Swing GUI编程,但它的功能足以编译服务器端和命令行应用程序。
该流程有一个明显缺陷:跨平台的代码在一步中必须绑定到一个平台。此外,静态编译并非是HotSpot的动态编译所擅长的。笔者曾参与一个项目,结果发现,与HotSpot版本相比,GCJ带来的性能提升不到5%。尽管如此,GCJ还是可以解决一些重要问题,如部署可运行的Java应用程序,而不必担心JVM是否可用或者在运行特定的版本。
Java Community Process
Java领域除了语言、库和虚拟机外,还有一个Java社区。尽管有大量用Java编写的开源软件,但在整个Java社区与开源社区之间仍然存在着公开且明显的矛盾。这在很大程度上可归咎于Sun不愿在适当的开源许可下发布它的Java实现,虽然这些源代码可在各种Sun指定的许可下获得。
有人说,这种冲突被大大地误导了。开发人员Bruno Souza在O'Reilly最新一期的Distributing the Future播客中讲到,这种反对Sun的争论完全误解了Java的性质,因为语言、库和虚拟机都是由开放且透明的Java Community Process制定的标准集:“其中的所有Java标准都被实现为开源软件。至于Sun之外的组织是否执行Java标准,我认为这区别不大。最重要的是,JCP的规则非常清楚。JCP是非常开放的标准组织。当然,它并不完美。但我认为一个非常重要的事实是,JCP创建标准,而您可以实现这些Java标准的开源形式。这极为重要,因为这就是我们想要的组合.... ”。
有人会说Java不是开源的,这样说毫无意义。因为说Java是或不是开源的并没有什么实际意义。这就像说HTTP是不是开源的一样没有实际意义。
实际上,Apache Harmony项目正在开发旨在成为“获得全球认证”的J2SE实现,它可在Apache License V2许可下获得,而且JCP允许并鼓励所有这些工作。
JCP之外的社区
但是,仍然存在许多并未采用JCP标准的Java项目。如前所述,Java是开发SourceForge项目的首选语言,但在java.net、Apache Jakarta Project、Javalobby的Javaforge、OpenSymphony以及其它无数独立站点上可找到更多的开源Java项目。在人们心目中,其中许多项目已经成熟,可与官方的JCP标准进行竞争,显然其中大部分是轻量级企业框架,如Spring framework,它诱使许多开发人员对EJB 2.1之类的“官方”规范感到失望。独立项目也快速适应了Java外部的变化,并产生了它们的最佳特性,如Rails,它就是改进后的Trails,或者如AJAX,它简化了Direct Web Remoting (DWR)项目。
结束语
十年来,数百万开发人员已使Java的面貌发生了巨大变化。现在需要推翻以往有关Java的假设了:语言与虚拟机之间的耦合、它与开源领域对立的错误描述,以及常见的对其性能或缺点的批评。在未来十年内,Java将变得完全不同,且发生巨大变化的潜力不在语言本身,而在于关注点。届时,许多开发人员将继续在不同的环境中使用不断发展的Java语言,而其他人则将在虚拟机上运行许多种不同的语言。不久以后,“什么是Java”的问题就会转变为“哪个Java?语言还是虚拟机?”这个问题。
毕竟,情况已经改变。
每个涉及applet和实时(Just-in-time)编译器的含糊定义都有许多已固定下来并为许多人所了解的新说明和新事实,但它们并非全部都写入文档中了。过去,Java常常意味着:
•Applet
•字节码解释
•缓慢的性能
•等待Sun恩赐的“拜物教”
而如今,它意味着:
•Web应用程序、Web服务、SOA等等
•热点动态编译
•高性能
•一个日益独立于Sun的开源社区
过去有个口号叫做“一次编写,随处运行”,这仍然是事实,但编写的内容及其运行环境和方式正在改变。
Java编程语言
Java是一种面向对象的高级编程语言,它在许多方面受到C、C++和Smalltalk的影响,还借用了其他语言的概念。其语法的设计方式使得那些熟悉“大括号”语言(继承自C)的人也会熟悉Java语法,但它具有比C++更强的面向对象性、对象的静态类型转换以及相当严格的异常系统,该系统要求调用堆栈中的每个方法要么处理异常,要么声明其抛出异常的能力。当然还有垃圾自动收集功能,这使开发人员不必释放由废弃对象占用的内存。
Java的一个比较受争议的方面(这些方面在发布Java时被广为接受,但现在正日益受到批评)是它的不完全的面向对象性。具体来说,Java基本类型(如int、char、boolean等等)都不是对象,并且开发人员需要以完全不同的方式来处理它们:由于int不是类,因此不能为其创建子类并为其声明新方法,也不能将它传递给需要普通对象的方法,诸如此类。基本类型提高了Java的性能,但却降低了代码的清晰度,这一点使用所谓的“包装器类”(Integer、Character和Boolean)的人应该深有体会。Java 5.0引入了autoboxing(自动装箱)模式,以消除许多使用包装器类的用例,但在某些方面这使代码的功能不那么明显了。
从理论上讲,Java是种“早期出错”语言。由于它的语法约束,许多编程错误在Java中不可能出现。由于不能直接访问指针,所以指针运算错误也就不存在了。使用对象时的类型如果与当初声明它的类型不同,就会要求进行显式的类型转换,这使编译器能够拒绝不合逻辑的编程,如对一幅图像调用一个字符串方法。
许多Java企业框架都要求使用配置文件或者部署描述符(通常用XML编写)来指定操作:哪个类处理特定的HTTP请求、在规则引擎中执行的步骤顺序等等。实际上,要实现这些功能不能只用这种语言。评论人士指出,这会产生不当后果:不仅避开了Java编译器的检查,而且开发人员无法再(只)根据程序的源代码就可确定它如何运行。Java 5.0为该语言添加了注释(annotation)特性,它允许使用值为方法、字段和类添加标签,在运行时,通常可通过反射对这些值进行内省和操作。许多程序员喜欢注释,因为它简化了工作,否则就需要通过部署描述符或其他方法来解决问题。但是,注释也有可能使Java代码难以理解,因为注释的有无可能会影响代码的执行方式,而这从注释中不太容易看出来。
尽管存在这么多的批评意见,但Java通常还是被认为是当今最流行的通用计算语言。在企业编程领域,它是一个广泛使用的标准,而且2005年它取代C++成为SourceForge项目使用最多的语言。使用Java有很多好处:免费的工具(适用于多种平台:Linux、Windows、Solaris和Mac均可编译和执行Java应用程序)、内容丰富的知识库以及大量乐意提供帮助的开发人员。
Java语言已经达到了开发人员生产率与代码性能之间的一个特定平衡点:CPU周期成本持续降低,但开发人员的开发周期却并未明显缩短,因此在开发人员与CPU操作码执行之间再出现一个抽象层也许是不可避免的了,它将使开发人员能够更快地创建更好的软件。实际上,Java生产率的批评者(如《Beyond Java》的Bruce Tate)可能正是观察到了这种不断推进Java使其达到新的平衡点,从而进一步牺牲性能去换取更高的开发人员生产率的趋势。
Java平台
通常有三种Java平台:Standard Edition(标准版,SE)、Enterprise Edition(企业版,EE)和Micro Edition(微型版)。每个平台都是一个包含某个语言版本、一组标准库和执行代码的虚拟机(见下文)的组合。EE是SE的超集,任何EE应用程序都可假定所有的SE库都存在。EE平台的语言使用与SE的一样。
由于小型设备(如:电话或机顶盒)的局限性,Java Micro Edition与另两个版本有很大区别。它并非SE的子集(像SE是EE的子集那样),因为它的一些库只存在于Micro Edition中。而且,ME取消了一些语言特性,如float类型和Float类,这反映了它的运行平台的局限性。ME需要与SE和EE不同的工具,而且设备之间的巨大差异使ME领域代码的可移植性更加不现实,因此许多Java开发人员将ME视为异类。
Java虚拟机
在某种程度上,Java源代码需要成为平台自带的可执行代码。这个过程一般需要两个步骤:开发人员将源代码编译成Java字节码,然后Java虚拟机(JVM)将其转换为主机平台的本地代码。第二步最初是通过解释方式执行的:读取每条JVM指令,然后动态地将其转换为一条或多条本地指令。然后,在程序开始运行时,实时(just-in-time,JIT)编译器将所有的Java程序从JVM字节码转换为本地代码。如今,该过程有多种实现方式。Sun的HotSpot编译器在运行时解释并分析代码,编译并优化对程序的操作最为关键的那部分。IBM的JVM工作原理与此非常类似。这些方法避免了由于对整个程序进行实时编译所导致的启动时性能下降,随着时间的推移,性能将会恢复,因为关键的代码部分已被定位并优化。长时间运行的服务器进程很适合采用这种方法,但这对客户机应用程序不太适用。
就像基本类型一样,现在批评人士认为Java的这个两步编译周期是一种不成熟的优化方法。他们提出疑问:如果要等到运行时将Java字节码编译为本地代码,那么为何不采用解释Java源代码(而非Java字节码)的方式,从而为开发人员节省一个步骤?正如Tate在《Beyond Java》一书中所说的那样,“Java并不是最简单的语言。它对很短的迭代也不友好……其他语言允许轻松地应用更改,而无需麻烦的编译/部署周期。”
没有Java的JVM
实际上,Tate在寻找秉承Java成功表现的后继者的过程中抱有这样的理念:“下一个在商业上取得成功的语言应该拥有在JVM上运行的版本。这将有助于该语言克服许多障碍,不管是在策略上还是在技术上。”他指出,虚拟机方法可提供安全性(“如果能确保虚拟机的安全性,则要确保语言的安全性就容易得多了”)、可移植性、互操作性和可扩展性。由于JVM已有效地解决了这些问题,因此如果新语言可运行在已安装于数百万台计算机中的JVM上,那么它就不需要自己的虚拟机。
在许多方面,这种情况业已发生。用Java为脚本语言编写解释器可有效地将这些语言移植到JVM上,如:用于JavaScript的Rhino、用于Python的Jython或者用于Ruby的JRuby。
但也可以完全绕过Java语言,而直接进入JVM级别。已经有一些将C转换为JVM字节码的编译器,如商业工具Axiomatic Multi-Platform C,它提供了ANSI C的子集。而且,Java字节码处理工具(如ASM和Apache BCEL)的发展允许Java应用程序在运行时创建可执行的类。这些类不再是Java语言,而是一种用于JVM编程的有效汇编语言。
或许由于意识到了在JVM上运行非Java代码的需求,最近已提交了一项新的JSR(Java规范请求),即“Supporting Dynamically Typed Languages on the Java Platform(在Java平台上支持动态类型化语言)”(JSR 292),它指定了一种新的字节码,将使JVM更适用于运行不含静态类型信息的语言。
没有JVM的Java
也可以从另一个角度来看问题,即不使用JVM而运行Java。毕竟从某种意义上讲,Java源代码转换为字节码,反过来字节码又转换为本地代码,没人会说这些转换不能一次完成。GNU Compiler for Java (GCJ)允许一次性地将Java源代码编译为一个平台的可执行代码。不过它尚不完善,不支持Abstract Windowing Toolkit(抽象窗口操作工具包,AWT),因此它不适用于AWT或Swing GUI编程,但它的功能足以编译服务器端和命令行应用程序。
该流程有一个明显缺陷:跨平台的代码在一步中必须绑定到一个平台。此外,静态编译并非是HotSpot的动态编译所擅长的。笔者曾参与一个项目,结果发现,与HotSpot版本相比,GCJ带来的性能提升不到5%。尽管如此,GCJ还是可以解决一些重要问题,如部署可运行的Java应用程序,而不必担心JVM是否可用或者在运行特定的版本。
Java Community Process
Java领域除了语言、库和虚拟机外,还有一个Java社区。尽管有大量用Java编写的开源软件,但在整个Java社区与开源社区之间仍然存在着公开且明显的矛盾。这在很大程度上可归咎于Sun不愿在适当的开源许可下发布它的Java实现,虽然这些源代码可在各种Sun指定的许可下获得。
有人说,这种冲突被大大地误导了。开发人员Bruno Souza在O'Reilly最新一期的Distributing the Future播客中讲到,这种反对Sun的争论完全误解了Java的性质,因为语言、库和虚拟机都是由开放且透明的Java Community Process制定的标准集:“其中的所有Java标准都被实现为开源软件。至于Sun之外的组织是否执行Java标准,我认为这区别不大。最重要的是,JCP的规则非常清楚。JCP是非常开放的标准组织。当然,它并不完美。但我认为一个非常重要的事实是,JCP创建标准,而您可以实现这些Java标准的开源形式。这极为重要,因为这就是我们想要的组合.... ”。
有人会说Java不是开源的,这样说毫无意义。因为说Java是或不是开源的并没有什么实际意义。这就像说HTTP是不是开源的一样没有实际意义。
实际上,Apache Harmony项目正在开发旨在成为“获得全球认证”的J2SE实现,它可在Apache License V2许可下获得,而且JCP允许并鼓励所有这些工作。
JCP之外的社区
但是,仍然存在许多并未采用JCP标准的Java项目。如前所述,Java是开发SourceForge项目的首选语言,但在java.net、Apache Jakarta Project、Javalobby的Javaforge、OpenSymphony以及其它无数独立站点上可找到更多的开源Java项目。在人们心目中,其中许多项目已经成熟,可与官方的JCP标准进行竞争,显然其中大部分是轻量级企业框架,如Spring framework,它诱使许多开发人员对EJB 2.1之类的“官方”规范感到失望。独立项目也快速适应了Java外部的变化,并产生了它们的最佳特性,如Rails,它就是改进后的Trails,或者如AJAX,它简化了Direct Web Remoting (DWR)项目。
结束语
十年来,数百万开发人员已使Java的面貌发生了巨大变化。现在需要推翻以往有关Java的假设了:语言与虚拟机之间的耦合、它与开源领域对立的错误描述,以及常见的对其性能或缺点的批评。在未来十年内,Java将变得完全不同,且发生巨大变化的潜力不在语言本身,而在于关注点。届时,许多开发人员将继续在不同的环境中使用不断发展的Java语言,而其他人则将在虚拟机上运行许多种不同的语言。不久以后,“什么是Java”的问题就会转变为“哪个Java?语言还是虚拟机?”这个问题。