友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
富士康小说网 返回本书目录 加入书签 我的书架 我的书签 TXT全本下载 『收藏到我的浏览器』

Java编程思想第4版[中文版](PDF格式)-第115部分

快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!



    }  

  }  

}  

  

正如大家看到的那样,服务器对象的实现是非常简单的;它是一个普通的 Java 类,从 IDL 编译器生成的 

 “干”代码中继承而来。但在与ORB 以及其他CORBA 服务进行联系的时候,情况却变得稍微有些复杂。  

  

4。 一些CORBA 服务  

这里要简单介绍一下JavaIDL 相关代码所做的工作(注意暂时忽略了CORBA 代码与不同厂商有关这一事 

实)。main()的第一行代码用于启动 ORB。而且理所当然,这正是服务器对象需要同它进行沟通的原因。就 

在ORB 初始化以后,紧接着就创建了一个服务器对象。实际上,它正式名称应该是“短期服务对象”:从客 

户那里接收请求,“生存时间”与创建它的进程是相同的。创建好短期服务对象后,就会通过ORB 对其进行 

注册。这意味着ORB 已知道它的存在,可将请求转发给它。  

到目前为止,我们拥有的全部东西就是一个timeServerObjRef——只有在当前服务器进程里才有效的一个对 

象引用。下一步是为这个服务对象分配一个字串形式的名字。客户会根据那个名字寻找服务对象。我们通过 

命名服务(Naming Service)完成这一操作。首先,我们需要对命名服务的一个对象引用。通过调用 

resolve_initial_references(),可获得对命名服务的字串式对象引用(在JavaIDL 中是 

 “NameService”),并将这个引用返回。这是对采用narrow()方法的一个特定NamingContext 引用的模 

型。我们现在可开始使用命名服务了。  

为了将服务对象同一个字串形式的对象引用绑定在一起,我们首先创建一个Nameponent 对象,用 

 “ExactTime”进行初始化。“ExactTime”是我们想用于绑定服务对象的名称字串。随后使用rebind()方 

法,这是受限于对象引用的字串化引用。我们用rebind()分配一个引用——即使它已经存在。而假若引用已 

经存在,那么bind()会造成一个异常。在CORBA 中,名称由一系列NameContext 构成——这便是我们为什么 

要用一个数组将名称与对象引用绑定起来的原因。  



                                                                                         669 


…………………………………………………………Page 671……………………………………………………………

服务对象最好准备好由客户使用。此时,服务器进程会进入一种等候状态。同样地,由于它是一种“短期服 

务”,所以生存时间要受服务器进程的限制。JavaIDL 目前尚未提供对“持久对象”(只要创建它们的进程 

保持运行状态,对象就会一直存在下去)的支持。  

现在,我们已对服务器代码的工作有了一定的认识。接下来看看客户代码:  

  

import RemoteTime。*;  

import org。omg。CosNaming。*;  

import org。omg。CORBA。*;  

  

public class RemoteTimeClient {  

  public static void main(String args'') {  

    try {  

      // ORB creation and initialization:  

      ORB orb = ORB。init(args; null);  

      // Get the root naming context:  

      org。omg。CORBA。Object objRef =   

        orb。resolve_initial_references(  

          〃NameService〃);  

      NamingContext ncRef =   

        NamingContextHelper。narrow(objRef);  

      // Get (resolve) the stringified object   

      // reference for the time server:  

      Nameponent nc =   

        new Nameponent(〃ExactTime〃; 〃〃);  

      Nameponent path'' = {nc};  

      ExactTime timeObjRef =   

        ExactTimeHelper。narrow(  

          ncRef。resolve(path));  

      // Make requests to the server object:  

      String exactTime = timeObjRef。getTime();  

      System。out。println(exactTime);  

    } catch (Exception e) {  

      System。out。println(  

         〃Remote Time server error: 〃 + e);  

      e。printStackTrace(System。out);  

    }  

  }  

}  

  

前几行所做的工作与它们在服务器进程里是一样的:ORB 获得初始化,并解析出对命名服务的一个引用。  

接下来,我们需要用到服务对象的一个对象引用,所以将字串形式的对象引用直接传递给resolve()方法, 

并用narrow()方法将结果造型到 ExactTime 接口引用里。最后调用 getTime()。  

  

5。 激活名称服务进程  

现在,我们已分别获得了一个服务器和一个客户应用,它们已作好相互间进行沟通的准备。大家知道两者都 

需要利用命名服务绑定和解析字串形式的对象引用。在运行服务或者客户之前,我们必须启动命名服务进 

程。在JavaIDL 中,命名服务属于一个 Java 应用,是随产品配套提供的。但它可能与其他产品有所不同。 

JavaIDL 命名服务在 JVM 的一个实例里运行,并(默认)监视网络端口 900。  

  

6。 激活服务器与客户  

现在,我们已准备好启动服务器和客户应用(之所以按这一顺序,是由于服务器的存在是“短期”的)。若 

各个方面都设置无误,那么获得的就是在客户控制台窗口内的一行输出文字,提醒我们当前的时间是多少。 



                                                                                           670 


…………………………………………………………Page 672……………………………………………………………

当然,这一结果本身并没有什么令人兴奋的。但应注意一个问题:即使都处在同一台机器上,客户和服务器 

应用仍然运行于不同的虚拟机内。它们之间的通信是通过一个基本的集成层进行的——即ORB 与命名服务的 

集成。  

这只是一个简单的例子,面向非网络环境设计。但通常将 ORB 配置成“与位置无关”。若服务器与客户分别 

位于不同的机器上,那么ORB 可用一个名为“安装库”(Implementation Repository)的组件解析出远程字 

串式引用。尽管“安装库”属于CORBA 的一部分,但它几乎没有具体的规格,所以各厂商的实现方式是不尽 

相同的。  

正如大家看到的那样,CORBA 还有许多方面的问题未在这儿进行详细讲述。但通过以上的介绍,应已对其有 

一个基本的认识。若想获得 CORBA 更详细的资料,最传真的起点莫过于OMB Web 站点,地址是 

http://omg。org 。这个地方提供了丰富的文档资料、白页、程序以及对其他CORBA 资源和产品的链接。  



A。6。3 Java 程序片和 CORBA  



Java 程序片可扮演一名 CORBA 客户的角色。这样一来,程序片就可访问由CORBA 对象揭示的远程信息和服 

务。但程序片只能同最初下载它的那个服务器连接,所以程序片与它沟通的所有 CORBA 对象都必须位于那台 

服务器上。这与CORBA 的宗旨是相悖的:它许诺可以实现“位置的透明”,或者“与位置无关”。  

将Java 程序片作为 CORBA 客户使用时,也会带来一些安全方面的问题。如果您在内联网中,一个办法是放宽 

对浏览器的安全限制。或者设置一道防火墙,以便建立与外部服务器安全连接。  

针对这一问题,有些Java ORB 产品专门提供了自己的解决方案。例如,有些产品实现了一种名为“HTTP 通 

道”(HTTP Tunneling)的技术,另一些则提供了特别的防火墙功能。  

作为放到附录中的内容,所有这些主题都显得太复杂了。但它们确实是需要重点注意的问题。  



A。6。4  比较 CORBA 与 RMI   



我们已经知道,CORBA 的一项主要特性就是对 RPC (远程过程调用)的支持。利用这一技术,我们的本地对象 

可调用位置远程对象内的方法。当然,目前已有一项固有的Java 特性可以做完全相同的事情:RMI (参考第 

15章)。尽管 RMI 使 Java 对象之间进行RPC 调用成为可能,但 CORBA 能在用任何语言编制的对象之间进行 

RPC。这显然是一项很大的区别。  

然而,可通过RMI 调用远程、非 Java 代码的服务。我们需要的全部东西就是位于服务器那一端的、某种形式 

的封装Java 对象,它将非 Java 代码“包裹”于其中。封装对象通过RMI 同Java 客户建立外部连接,并于内 

部建立与非 Java 代码的连接——采用前面讲到的某种技术,如 JNI 或J/Direct 。  

使用这种方法时,要求我们编写某种类型的“集成层”——这其实正是 CORBA 帮我们做的事情。但是这样做 

以后,就不再需要其他厂商开发的ORB 了。  



A。7  总结  



我们在这个附录讨论的都是从一个 Java 应用里调用非Java 代码最基本的技术。每种技术都有自己的优缺 

点。但目前最主要的问题是并非所有这些特性都能在所有 JVM 中找到。因此,即使一个Java 程序能调用位于 

特定平台上的固有方法,仍有可能不适用于安装了不同JVM 的另一种平台。  

Sun 公司提供的 JNI 具有灵活、简单(尽管它要求对JVM 内核进行大量控制)、功能强大以及通用于大多数 

JVM 的优点。到本书完稿时为止,微软仍未提供对 JNI 的支持,而是提供了自己的J/Direct (调用Win32  

DLL 函数的一种简便方法)和RNI (特别适合编写高效率的代码,但要求对JVM 内核有很深入的理解)。微软 

也提供了自己的专利Java/ 集成方案。这一方案具有很强大的功能,且将 Java 变成了编写  服务器和 

客户的有效语言。只有微软公司的编译器和JVM 能提供对J/Direct 、RNI 以及Java/ 的支持。  

我们最后研究的是 CORBA,它使我们的Java 对象可与其他对象沟通——无论它们的物理位置在哪里,也无论 

是用何种语言实现的。CORBA 与前面提到的所有技术都不同,因为它并未集成到 Java 语言里,而是采用了其 

他厂商(第三方)的集成技术,并要求我们购买其他厂商提供的ORB。CORBA 是一种有趣和通用的方案,但如 

果只是想发出对操作系统的调用,它也许并非一种最佳方案。  



                                                                671 


…………………………………………………………Page 673……………………………………………………………

                        附录 B 对比 C++ 和 Java  



  

 “作为一名C++程序员,我们早已掌握了面向对象程序设计的基本概念,而且 Java 的语法无疑是非常熟悉 

的。事实上,Java 本来就是从 C++衍生出来的。”  

  

然而,C++和Java 之间仍存在一些显著的差异。可以这样说,这些差异代表着技术的极大进步。一旦我们弄 

清楚了这些差异,就会理解为什么说Java 是一种优秀的程序设计语言。本附录将引导大家认识用于区分 

Java 和C++的一些重要特征。  

(1) 最大的障碍在于速度:解释过的Java 要比C 的执行速度慢上约 20 倍。无论什么都不能阻止Java 语言进 

行编译。写作本书的时候,刚刚出现了一些准实时编译器,它们能显著加快速度。当然,我们完全有理由认 

为会出现适用于更多流行平台的纯固有编译器,但假若没有那些编译器,由于速度的限制,必须有些问题是 

Java 不能解决的。  

(2) 和C++一样,Java 也提供了两种类型的注释。  

(3) 所有东西都必须置入一个类。不存在全局函数或者全局数据。如果想获得与全局函数等价的功能,可考 

虑将 static方法和 static 数据置入一个类里。注意没有象结构、枚举或者联合这一类的东西,一切只有 

 “类”(Class)!  

(4) 所有方法都是在类的主体定义的。所以用 C++的眼光看,似乎所有函数都已嵌入,但实情并非如何(嵌 

入的问题在后面讲述)。  

(5) 在Java 中,类定义采取几乎和 C++一样的形式。但没有标志结束的分号。没有class foo 这种形式的类 

声明,只有类定义。  

  

class aType()  

void aMethod() {/* 方法主体 */}  

}  

  

(6) Java 中没有作用域范围运算符“::”。Java 利用点号做所有的事情,但可以不用考虑它,因为只能在一 

个类里定义元素。即使那些方法定义,也必须在一个类的内部,所以根本没有必要指定作用域的范围。我们 

注意到的一项差异是对 static方法的调用:使用 ClassName。methodName() 。除此以外,package (包)的名 

字是用点号建立的,并能用 import关键字实现 C++的“#include ”的一部分功能。例如下面这个语句:  

import java。awt。*;  

 (#include 并不直接映射成 import,但在使用时有类似的感觉。)  

(7) 与C++类似,Java 含有一系列“主类型”(Primitive type),以实现更有效率的访问。在Java 中,这 

些类型包括 boolean,char,byte ,short,int,long,float 以及double。所有主类型的大小都是固有 

的,且与具体的机器无关(考虑到移植的问题)。这肯定会对性能造成一定的影响,具体取决于不同的机 

器。对类型的检查和要求在Java 里变得更苛刻。例如:  

■条件表达式只能是boolean (布尔)类型,不可使用整数。  

■必须使用象X+Y 这样的一个表达式的结果;不能仅仅用“X+Y”来实现“副作用”。  

(8) char (字符)类型使用国际通用的16位 Unicode 字符集,所以能自动表达大多数国家的字符。  

(9) 静态引用的字串会自动转换成 String 对象。和C 及 C++不同,没有独立的静态字符数组字串可供使用。  

(10) Java增添了三个右移位运算符“》》》”,具有与“逻辑”右移位运算符类似的功用,可在最末尾插入零 

值。“》》”则会在移位的同时插入符号位(即“算术”移位)。  

(11) 尽管表面上类似,但与 C++相比,Java 数组采用的是一个颇为不同的结构,并具有独特的行为。有一个 

只读的 length 成员,通过它可知道数组有多大。而且一旦超过数组边界,运行期检查会自动丢弃一个异常。 

所有数组都是在内存“堆”里创建的,我们可将一个数组分配给另一个(只是简单地复制数组句柄)。数组 

标识符属于第一级对象,它的所有方法通常都适用于其他所有对象。  

(12) 对于所有不属于主类型的对象,都只能通过new 命令创建。和 C++不同,Java 没有相应的命令可以“在 

堆栈上”创建不属于主类型的对象。所有主类型都只能在堆栈上创建,同时不使用new 命令。所有主要的类 

都有自己的“封装(器)”类,所以能够通过 new 创建等价的、以内存“堆”为基础的对象(主类型数组是 

一个例外:它们可象C++那样通过集合初始化进行分配,或者使用 new)。  

(13) Java 中不必进行提前声明。若想在定义前使用一个类或方法,只需直接使用它即可——编译器会保证 



                                                                       672 


…………………………………………………………Page 674……………………………………………………………

使用恰当的定义。所以和在C++中不同,我们不会碰到任何涉及提前引用的问题。  

(14) Java没有预处理机。若想使用另一个库里的类,只需使用 import 命令,并指定库名即可。不存在类似 

于预处理机的宏。  

(15) Java用包代替了命名空间。由于将所有东西都置入一个类,而且由于采用了一种名为“封装”的机 

制,它能针对类名进行类似于命名空间分解的操作,所以命名的问题不再进入我们的考虑之列。数据包也会 

在单独一个库名下收集库的组件。我们只需简单地“import”(导入)一个包,剩下的工作会由编译器自动 

完成。  

(16) 被定义成类成员的对象句柄会自动初始化成null 。对基本类数据成员的初始化在Java 里得到了可靠的 

保障。若不明确地进行初始化,它们就会得到一个默认值(零或等价的值)。可对它们进行明确的初始化 

 (显式初始化):要么在类内定义它们,要么在构建器中定义。采用的语法比C++的语法更容易理解,而且 

对于 static和非 static成员来说都是固定不变的。我们不必从外部定义 static成员的存储方式,这和 C++ 

是不同的。  

(17) 在Java 里,没有象 C 和C++那样的指针。用new 创建一个对象的时候,会获得一个引用(本书一直将 

其称作“句柄”)。例如:  

String s = new String(〃howdy〃);  

然而,C++引用在创建时必须进行初始化,而且不可重定义到一个不同的位置。但Java 引用并不一定局限于 

创建时的位置。它们可根据情况任意定义,这便消除了对指针的部分需求。在C 和C++里大量采用指针的另 

一个原因是为了能指向任意一个内存位置(这同时会使它们变得不安全,也是Java 不提供这一支持的原 

因)。指针通常被看作在基本变量数组中四处移动的一种有效手段。Java 允许我们以更安全的形式达到相同 

的目标。解决指针问题的终极方法是“固有方法”(已在附录A 讨论)。将指针传递给方法时,通常不会带 

来太大的问题,因为此时没有全局函数,只有类。而且我们可传递对对象的引用。Java 语言最开始声称自己 

 “完全不采用指针!”但随着许多程序员都质问没有指针如何工作?于是后来又声明“采用受到限制的指 

针”。大家可自行判断它是否“真”的是一个指针。但不管在何种情况下,都不存在指针“算术”。  

(18) Java提供了与C++类似的“构建器”(Constructor)。如果不自己定义一个,就会获得一个默认构建 

器。而如果定义了一个非默认的构建器,就不会为我们自动定义默认构建器。这和C++是一样的。注意没有 

复制构建器,因为所有自变量都是按引用传递的。  

(19) Java 中没有“破坏器”(Destructor)。变量不存在“作用域”的问题。一个对象的“存在时间”是 

由对象的存在时间决定的,并非由垃圾收集器决定。有个 finalize()方法是每一个类的成员,它在某种程度 

上类似于C++的“破坏器”。但 finalize()是由垃圾收集器调用的,而且只负责释放“资源”(如打开的文 

件、套接字、端口、URL 等等)。如需在一个特定的地点做某样事情,必须创建一个特殊的方法,并调用 

它,不能依赖finalize()。而在另一方面,C++中的所有对象都会(或者说“应该”)破坏,但并非Java 中 

的所有对象都会被当作“垃圾”收集掉。由于 Java 不支持破坏器的概念,所以在必要的时候,必须谨慎地创 

建一个清除方法。而且针对类内的基础类以及成员对象,需要明确调用所有清除方法。  

(20) Java具有方法“过载”机制,它的工作原理与 C++函数的过载几乎是完全相同的。  

(21) Java不支持默认自变量。  

(22) Java 中没有goto 。它采取的无条件跳转机制是“break 标签”或者“continue 标准”,用于跳出当前 

的多重嵌套循环。  

(23) Java 采用了一种单根式的分级结构,因此所有对象都是从根类 Object 统一继承的。而在 C++中,我们 

可在任何地方启动一个新的继承树,所以最后往往看到包含了大量树的“一片森林”。在Java 中,我们无论 

如何都只有一个分级结构。尽管这表面上看似乎造成了限制,但由于我们知道每个对象肯定至少有一个 

Object 接口,所以往往能获得更强大的能力。C++ 目前似乎是唯一没有强制单根结构的唯一一种OO 语言。  

(24) Java没有模板或者参数化类型的其他形式。它提供了一系列集合:Vector (向量),Stack (堆栈)以 

及Hashtable (散列表),用于容纳Object 引用。利用这些集合,我们的一系列要求可得到满足。但这些集 

合并非是为实现象 C++ “标准模板库”(STL)那样的快速调用而设计的。Java 1。2 中的新集合显得更加完 

整,但仍不具备正宗模�
返回目录 上一页 下一页 回到顶部 赞(11) 踩(15)
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!