2.4 网络移动性
长久以来,如何开发网络软件是Java开发人员所面临的最大挑战之一。在网络领域需要实现平台无关性,因为同一网络中通常连接了多种不同的计算机和设备。除此之外,安全模式也是一个挑战,因为网络可以方便地传输病毒和其他形式的恶意代码。在本节将详细讲解Java如何把握网络所带来的巨大机遇,为步入本书后面知识的学习打下基础。
2.4.1 现实需要网络移动性
当个人计算机互联成网变得越来越普遍的时候,另一种软件模式日益重要起来,即“客户机/服务器”模式。“客户机/服务器”模式将任务分为两部分,分别运行在两种计算机上:客户端进程运行在终端用户的个人计算机上,而服务器端进程运行在同一网络的另一台计算机上。客户端和服务器端的进程通过网络来回发送数据进行传输。服务器端进程通常只是简单地接收网络中客户端发来的数据请求命令,从中央数据库中提取需要的数据,并将该数据发送给客户端。而客户端在接到数据后,进行处理,然后显示并允许用户操作数据。这样的模式允许个人计算机的终端用户读取并操作放在中央储藏库的数据,而不需强迫这些用户共享中央CPU来处理数据。终端用户地区是共享了运行服务器端进程的CPU,但在一定程度上,数据处理是由客户端完成的,因此大大减轻了服务器端CPU的负载。
“客户机/服务器”模式最初被称作两层客户机/服务器模式,一层是客户端,另一层是服务器。更复杂一些的模型叫做3层(表示有3个进程)、4层(4个进程)或者N层结构,也就是说层次结构越来越多了。当更多的进程加入计算时,客户端和服务器的区别模糊了,于是人们开始使用“分布式处理”这个新名词来涵盖所有这些结构模式。
分布式处理模式综合了网络和处理器发展的优点,将进程分布在多个处理器上运行,并允许这些进程共享数据。尽管这种模式有许多大型计算机系统所无法比拟的优势,但它也有个不可忽视的缺点:分布式处理比大型计算机系统更难管理。在大型计算机系统中,软件应用程序存储在主机的磁盘上,虽然可以有多个用户使用该软件,但它只需在一个地方安装和维护。升级一个软件后,所有用户在下一次登录并启动该软件的时候可以得到这个新的版本。但是相反,在分布式系统中,不同组件的软件往往存储在不同的磁盘上,因此,系统管理员需要在分布式系统的不同组件上安装和维护软件。要升级一个软件时,管理员不得不分别升级每台计算机上的这个软件。所以,分布式处理的系统管理比大型计算机系统要困难得多。
Java的体系结构使软件的网络移动性成为可能,同时也预示了一种新的计算模式的到来。这种新的模式建立在流行的分布式处理模式的基础上,并可以将软件通过网络自动传送到各台计算机上。这样就解决了分布式处理系统中系统管理的困难。例如在一个C/S系统中,客户端软件可以存储在网络中的一台中央计算机上,当终端用户需要用该软件的时候,这个中央计算机会通过网络将可执行的软件传送到终端用户的计算机上运行。
因此,软件的网络移动性标志着计算模式发展历程中的重要一步,尤其是它解决了分布式处理系统中系统管理的问题,简化了将软件分布在多台CPU上的工作,使数据可以和相关软件一起被传送。
2.4.2 网络移动性
平台无关性使得在网络上传送程序更加容易,因为不需要为每个不同的主机平台都准备一个单独的版本,因此也不需要判断每台计算机需要哪个特定的版本,一个版本就可以对付所有的计算机。Java的安全特性促进了网络移动性的推广,因为最终用户就算从不信任的来源下载class文件,也可以充满自信。因此实际上,Java体系结构通过对平台无关性和安全性的支持,更好地推广了其class文件的网络机动性。
除了平台无关性和安全性之外,Java体系结构对网络移动性的支持主要集中在对在网络上传送程序的时间进行管理上。假若你在服务器上保存了一个程序,在需要的时候通过网络来下载它,这个过程一般都会比从本地执行该程序要慢。因此对于在网络上传送程序来说,网络移动性的一个主要难题就是时间。Java体系结构通过把传统的单一二进制可执行文件切割成小的二进制碎片——Javaclass文件——来解决这个问题。class文件可以独立在网络上传播,因为Java程序是动态链接、动态扩展的,最终用户不需要等待所有的程序class文件都下载完毕,就可以开始运行程序了。第一个class文件到手,程序就开始执行。class文件本身也被设计得很紧凑,所以它们可以在网络上飞快地传送。因此Java体系结构为网络移动性带来的直接主要好处就是把一个单一的大二进制文件分割成小的class文件,这些class文件可以按需装载。
Java应用程序从某个类的main()方法开始执行,其他的类在程序需要的时候才动态链接。如果某个类在一次操作中没有被用到,这个类就不会被装载。比如说,假若你在使用一个字处理程序,它有一个拼写检查器,但是在你使用的这次操作中没有使用拼写检查器,那么它就不会被装载。
除了动态链接之外,Java体系结构也允许动态扩展。动态扩展是装载class文件的另一种方式,可以延迟到Java应用程序运行时才装载。使用用户自定义的类装载器,或者Class类的forName()方法,Java程序可以在运行时装载额外的程序,这些程序就会变成运行程序的一部分。因此,动态链接和动态扩展给了Java程序员一些设计上的灵活性,既可以决定何时装载程序的class文件——而这又决定了最终用户需要等待多少时间来从网络上装载class文件。
除了动态连接和动态扩展,Java体系结构对网络移动性的直接支持还通过class文件格式体现。为了减少在网络上传送程序的时间,class文件被设计得很紧凑。它们包含的字节码流设计得特别紧凑——之所以被称为“字节码”,是因为每条指令都只占据一个字节。除了两个例外情况,所有的操作码和它们的操作数都是按照字节对齐的,这使得字节码流更小。这两个例外是这样一些操作码,在操作码和它们的操作数之间会填上1~3个字节,一边操作数都按照字边界对齐。
class文件的紧凑型隐含着另外一个含义,那就是Java编译器不会做太多的局部优化。因为二进制兼容性规则的存在,Java编译器不能做一些全局优化,比如把一个方法调用转化为整个方法的内嵌(内嵌指把被调用方法的整个方法体都替换到发起调用的方法中去,这样在代码运行的时候,可以节省方法调用和返回的时间)。二进制兼容性要求,假若一个方法被现有class文件包括以后,那么改变这个方法的时候必须不破坏已有的调用方法。在同一个类中使用的方法可能使用内嵌,但是一般来说,Java编译器不会做这种优化,部分原因是这样为class文件瘦身得不偿失。优化常常是在代码大小和执行速度间进行的折中。因此,Java编译器通常会把优化工作留给Java虚拟机,后者在装载类之后,在解释执行,即时编译或者自适应编译的时候都可以优化代码。
除了动态链接、动态扩展和紧凑的class文件之外,还有一些并非体系结构必须的策略,可以帮助控制在网络上传送class文件的时间。因为HTTP需要单独为Javaapplet中用到的每一个class文件请求连接,所以下载applet的很大一部分时间并不是用来实际传输class文件的时间,而是每一个class文件请求的网络协议握手的时间。一个文件需要的总时间是按照需要下载的class文件的数目倍增的。为了解决这个问题,Java 1.1包含了对Jar的支持,Jar文件允许在一次网络传输过程中传输多个文件,这和一次传送一个个单独class文件相比,大幅度降低了需要的总体下载时间。更大的优点是,Jar文件中的数据可以压缩,从而使下载时间更少。所以有时候通过一个大文件来传送软件。例如有些class文件是程序开始运行之前所必需的,这些文件可以很快地通过Jar文件一次性传递。
另外一个降低最终用户等待时间的策略就是不采取按需下载class文件的做法。有几种不同的技术,例如MarimbaCastanet使用的订阅模式,可以在需要class文件之前就已经把它们下载下来了,这样程序就可以更快地启动。
因此,除了平台无关性和安全性能够对网络移动性有利外,Java体系结构的主要着眼点就是控制class文件在网络上传送的时间。动态链接和动态扩展允许Java程序按照小功能单元设计,在最终用户需要的时候才单独下载。Class文件的紧凑性本身有助于减少Java程序在网络上传送的时间。Jar文件允许在一次网络连接中传送多个文件,还允许数据压缩。