第 7 章

来源:百度文库 编辑:神马文学网 时间:2024/03/29 19:05:00
远程对象激活
----------------------------------------------------------------------
---------- 第 7 章 主题:
概述
激活协议
“可激活”远程对象的实现模型
激活接口 .1 概述
分布式对象系统被设计为支持长期存在的持久对象。假设这些系统将由成千(也
许成万)个这样的对象组成,则对象的实现在无限期的时间段内被激活并保持活
动状态是不合理的。这将占用宝贵的系统资源。另外,客户机需要保存对对象的
持久引用的能力,这样在一个系统崩溃后可以重新建立对象之间的通讯,因为通
常对一个分布对象的引用只有当对象处于活动状态时才有效。 对象激活是一种用来提供对对象持久引用和管理对象实现的执行的机制。在 RMI
中,激活允许对象根据需要开始执行。当访问(通过方法调用)“可激活”远程
对象时,如果该远程对象当前尚未执行,则系统将在适当的 Java 虚拟机中开始
该对象的执行。 7.1.1 术语
活动 (active) 对象是在某些系统的 Java 虚拟机中被实例化并被导出的远程对
象。非活动对象在虚拟机中尚未被实例化(或导出),但能变成活动状态的远程
对象。激活就是将非活动对象转化为活动对象的过程。激活要求对象与一台虚拟
机相关联,而这将可能需要将该对象的类加载到虚拟机中,同时该对象也恢复其
持久状态(如果有)。 在 RMI 系统中,我们使用了惰性激活。惰性激活就是将一个对象的激活延迟到客
户机第一次使用该对象时(即第一次方法调用时)。 7.1.2 惰性激活
远程对象的惰性激活是用不完善远程引用(有时称为不完善块)实现的。对远程
对象的不完善远程引用在第一次调用对象的方法时,“完善”为活动对象的引用
。每个不完善引用均保持一个持久句柄(激活标识符)和对目标远程对象的瞬态
远程引用。远程对象的激活标识符包含足够的信息来使第三方激活该对象。瞬态
引用是对可用来联系执行对象的主动远程对象的“活动”引用。 在不完善引用中,如果对远程对象的活引用为空,则不认为目标对象是主动的。
在方法调用中,不完善引用(对该对象)将加入激活协议以获得“活”引用,该
引用是对新激活的对象的远程引用(例如单路传送 (unicast) 的远程引用)。一
旦不完善引用得到活引用,则不完善引用将把方法调用传给底层的远程引用,而
该远程引用又将方法调用传给远程对象。 具体的说,远程对象的 stub 包含一个“不完善”远程引用类型,该类型既包括: 远程对象的激活标识符,又包括
“活”引用(可能为空),其中包含远程对象的“活动”远程引用类型(例如,
带有单路传送语义的远程引用类型)。 ----------------------------------------------------------------------
---------- 注意 - RMI 系统对远程调用保留“至多一次”语义。换句话说,对可激活或单路
传送远程对象的调用将至多发送一次。因此,如果对远程对象的调用失败(由抛
出的 RemoteException 异常表示),则客户机将得到如下保证:远程方法的执行
不会超过一次,甚至根本就不执行。 7.2 激活协议
在远程方法调用期间,如果目标对象的“活”(live) 引用是未知的,则不完善引
用将使用激活协议。激活协议包括下列几个实体:不完善引用、激活器、Java 虚
拟机中的激活组和被激活的远程对象。 激活器(通常每个主机有一个)是一个实体,负责激活,它是: 将激活标识符映射到激活对象所需信息(对象的类、位置 -- URL 路径 -- 从该
处可加载类、对象可能需要用于自举 (bootstrap) 的特定数据等)的信息数据库,及
Java 虚拟机的管理器,它启动虚拟机(必要时)并将对象激活请求(和必要的信
息一起)传送到远程虚拟机中正确的激活组。
注意:激活器始终将激活标识符到活动对象的当前映射保存在缓存中,这样就无
需为每个激活请求而查询该组。 激活组(每个 Java 虚拟机中一个)是这样的实体,它接收对激活 Java 虚拟机
中对象的请求并将激活的对象返给激活器。 激活协议如下所示。不完善引用使用一个激活标识符并调用激活器(内部 RMI 接
口)来激活与该标识符关联的对象。激活器查找对象的激活描述符(先前已注册
)。对象的描述符包括: 对象的组标识符(指定对象激活时所处的虚拟机),
对象的类名,
URL 路径,从该处加载对象的类代码,
特定于对象的已编组的初始化数据(例如,初始化数据可能是包含对象持久状态
的文件的名称)。
如果应容纳该对象的激活组存在,则激活器将激活请求传送到该组。如果激活组
不存在,则激活器将启动虚拟机以执行激活组,然后将激活请求传送到该组。 激活组将加载对象的类并用特定的构造函数来实例化该对象。此构造函数带多个
参数,包括先前注册的激活描述符。 对象完成激活时,激活组将把已编组对象引用传回激活器,然后该激活器记录激
活标识符和激活引用对,并将活动(活)引用返给不完善引用。随后,不完善引
用(在 stub 内)通过活动引用将方法调用直接传给远程对象。
----------------------------------------------------------------------
----------
注意 - 在 JDK 中,RMI 提供激活系统接口的实现。要使用激活,必须首先运行
激活系统守护进程 (daemon) rmid。 7.3 “可激活”远程对象的实现模型
为了使可通过激活标识符访问的远程对象不受时间影响,开发人员必须做到: 为该远程对象注册一个激活描述符
在对象的类中包含一个专用构造函数,当 RMI 系统激活可激活对象时将调用它。 可用以下几种方法来注册激活描述符 (ActivationDesc): 调用类 Activatable 的静态 register 方法
用 Activatable 类的第一个或第二个构造函数创建“可激活”对象
显式地导出“可激活”对象。该过程可用 Activatable 的第一个或第二个 expo
rtObject 方法实现,其参数为 ActivationDesc、Remote 对象的实现和端口号。 对于特定对象,只可用上述三种方法之一来注册激活对象。有关如何实现可激活
对象的示例,请参阅后面的“构造可激活远程对象”。 7.3.1 ActivationDesc 类
ActivationDesc 含有激活对象所需的信息。它包含对象的激活组标识符、对象的
类名、加载对象代码的 codebase 路径(或 URL)及 MarshalledObject(可包含
每次激活期间所用的对象特定初始化数据)。 激活过程中将查询在激活系统中注册的描述符以获取有关的信息,从而用于重新
创建或激活对象。对象的描述符中的 MarshalledObject 将作为第二个参数传给
远程对象的构造函数,以供激活过程使用。 package java.rmi.activation; public final class ActivationDesc implements java.io.Serializable
{
public ActivationDesc(String className, String codebase,
java.rmi.MarshalledObject data)
throws ActivationException;
public ActivationDesc(String className, String codebase,
java.rmi.MarshalledObject data,
boolean restart)
throws ActivationException;
public ActivationDesc(ActivationGroupID groupID,
String className,
String codebase,
java.rmi.MarshalledObject data,
boolean restart);
public ActivationDesc(ActivationGroupID groupID,
String className,
String codebase,
java.rmi.MarshalledObject data);
public ActivationGroupID getGroupID();
public String getClassName();
public String getLocation();
public java.rmi.MarshalledObject getData()
public boolean getRestartMode();
} ActivationDesc 的第一个构造函数构造一个对象的对象描述符,这个对象的类是
className(可从 codebase 路径加载),它的初始化信息(已编组形式)为 d
ata。如果使用这种形式的构造函数,则对象的组标识符缺省为该虚拟机 Activa
tionGroup 的当前标识符。具有相同 ActivationGroupID 的所有对象都将在同一
虚拟机中被激活。如果当前组是非活动的或无法创建缺省组,则将抛出 Activat
ionException。如果 groupID 为 null,则将抛出 IllegalArgumentException。

----------------------------------------------------------------------
---------- 注意 - 作为创建 ActivationDesc 的副效应,如果该虚拟机的 ActivationGrou
p 当前不是活动的,则将创建缺省 ActivationGroup。缺省激活组将 java.rmi.
RMISecurityManager 作为安全管理器,并在重新激活时将激活组虚拟机中的属性
设置为该虚拟机中的当前属性。如果应用程序需用不同的安全管理器,则在创建
缺省 ActivationDesc 之前必须设置该虚拟机的组。有关如何为虚拟机创建 Act
ivationGroup 的详细信息,参见方法 ActivationGroup.createGroup。
----------------------------------------------------------------------
---------- ActivationDesc 第二个构造函数构造对象描述符的方式与第一个构造函数相同,
但必须提供附加的参数 restart。如果对象要求重启服务,这意味着当激活器重
新启动时,对象也会自动重新启动(与根据需要激活的惰性激活相反)。此时,
restart 应为 true。如果 restart 为 false,则对象将只是在需要时激活(通
过远程方法调用)。 ActivationDesc 的第三个构造函数构造一个对象的对象描述符。这个对象的组标
识符为 groupID,它的类名为 className(可从 codebase 路径加载),它的初
始化信息为 data。所有具有相同 groupID 的对象都将在同一 Java 虚拟机中被
激活。 ActivationDesc 第四个构造函数构造对象描述符的方式与第三个构造函数相同,
但它允许指定重启模式。如果对象需要重新启动(定义如上),则 restart 应为
true。 getGroupID 方法返回该描述符所指定对象的组标识符。组可以提供一种将对象聚
合到单一 Java 虚拟机中的方法。 getClassName 方法返回该激活描述符所指定对象的类名。 getLocation 方法返回用于下载该对象的类的 codebase 路径。 getData 方法返回一个“已编组对象”,该编组对象包含用于初始化(激活)描
述符指定的对象的数据。 getRestartMode 方法在该对象的重启模式启用时返回 true,否则返回 false。 7.3.2 ActivationID 类
激活协议利用激活标识符来表示不受时间影响的可激活远程对象。激活标识符(
类 ActivationID 的实例)含有激活对象所需的若干信息: 对象激活器的远程引用
对象的唯一标识符。
对象的激活标识符可通过向激活系统注册对象来得到。可用下述几种方法之一来
完成注册(如上所述): 通过 Activatable.register 方法
通过第一个或第二个 Activatable 构造函数(都有三个参数而且注册并导出对象

通过第一个或第二个 Activatable.exportObject 方法(它们以激活描述符、对
象实现及端口为参数,且都注册并导出对象)。 package java.rmi.activation; public class ActivationID implements java.io.Serializable
{
public ActivationID(Activator activator);
public Remote activate(boolean force)
throws ActivationException, UnknownObjectException, java.rmi.
RemoteExcep
tion;
public boolean equals(Object obj);
public int hashCode();
} ActivationID 的构造函数接受参数 activator,该参数指定对激活器(负责激活
与该激活标识符关联的对象)的远程引用。ActivationID 的实例是全局唯一的。
activate 方法将激活与该激活标识符关联的对象。如果 force 参数为 true,激
活器将把远程对象的任何高速缓存引用视为已过时,从而迫使激活器在激活对象
时与组联系。如果 force 为 false,则返回可接受的高速缓存值。如果激活失败
,则抛出 ActivationException。如果激活器不能识别该对象标识符,则该方法
将抛出 UnknownObjectException。如果对激活器的远程调用失败,则抛出
RemoteException。
equals 方法可实现基于内容的相等比较。如果所有的域都相等(完全相同或从各
域的 Object.equals 语义上等价),它将返回 true。如果 p1 和 p2 是类 Act
ivationID 的实例,则在 p1.equals(p2) 返回 true 时 hashCode 方法返回相同
的值。 7.3.3 Activatable 类
Activatable 类提供对需要持久访问而不受时间影响,同时又被系统激活的远程
对象的支持。类 Activatable 是开发人员用来实现和管理可激活对象的主要应用
程序接口。注意,必须先运行激活系统守护进程 rmid,然后才能注册和/或激活
对象。
package java.rmi.activation;
public abstract class Activatable
extends java.rmi.server.RemoteServer
{
protected Activatable(String codebase,
java.rmi.MarshalledObject data,
boolean restart,
int port)
throws ActivationException, java.rmi.RemoteException; protected Activatable(String codebase,
java.rmi.MarshalledObject data,
boolean restart,
int port,
RMIClientSocketFactory csf,
RMIServerSocketFactory ssf)
throws ActivationException, java.rmi.RemoteException; protected Activatable(ActivationID id, int port)
throws java.rmi.RemoteException; protected Activatable(ActivationID id, int port,
RMIClientSocketFactory csf,
RMIServerSocketFactory ssf)
throws java.rmi.RemoteException; protected ActivationID getID(); public static Remote register(ActivationDesc desc)
throws UnknownGroupException, ActivationException,
java.rmi.RemoteException; public static boolean inactive(ActivationID id)
throws UnknownObjectException, ActivationException,
java.rmi.RemoteException; public static void unregister(ActivationID id)
throws UnknownObjectException, ActivationException,
java.rmi.RemoteException; public static ActivationID exportObject(Remote obj,
String codebase,
MarshalledObject data,
boolean restart,
int port)
throws ActivationException, java.rmi.RemoteException; public static ActivationID exportObject(Remote obj,
String codebase,
MarshalledObject data,
boolean restart,
int port,
RMIClientSocketFactory csf,
RMIServerSocketFactory ssf)
throws ActivationException, java.rmi.RemoteException; public static Remote exportObject(Remote obj,
ActivationID id,
int port)
throws java.rmi.RemoteException; public static Remote exportObject(Remote obj,
ActivationID id,
int port,
RMIClientSocketFactory csf,
RMIServerSocketFactory ssf)
throws java.rmi.RemoteException; public static boolean unexportObject(Remote obj, boolean force)
throws java.rmi.NoSuchObjectException;
} 可激活远程对象的实现可能扩展或不扩展类 Activatable。确实扩展 Activatab
le 类的远程对象实现将从超类 java.rmi.server.RemoteObject 中继承 hashCo
de 和 equals 方法中的相应定义。因此,引用同一 Activatable 远程对象的两
个远程引用是相等的(equals 方法将返回 true)。同样,类 Activatable 的实
例将“等于”该实例的相应 stub 对象(即如果 Object.equals 方法用与该实现
匹配的 stub 对象作为参数进行调用,它将返回 true。反之亦然)。 Activatable 类方法
Activatable 类的第一个构造函数用于在指定 port 上注册和导出对象(如果 p
ort 为零,则选用某个匿名端口)。下载的类代码对象的 URL 路径是 codbase,
其初始化数据是 data。如果 restart 为 true,则对象将在激活器重新启动或组
失效时自动重新启动。如果 restart 为 false,则对象将根据需要而激活(通过
对对象的远程方法调用)。 Activatable 类的具体子类必须调用该构造函数才能在初始构造期间注册和导出
该对象。作为构造可激活对象时的副效应,远程对象将被“注册”到激活系统上
并“导出”(如果 port 为零,则在匿名端口导出)到 RMI 运行时,从而使之可
接受来自客户机的到来调用。 如果向激活系统注册对象失败,则该构造函数将抛出 ActivationException。如
果将对象导出到 RMI 运行时失败,则将抛出 RemoteException。 第二个构造函数与第一个 Activatable 构造函数相同,但它允许将客户机和服务
器套接字工厂规范用于与该可激活对象进行通讯。详细信息,参见“RMI 套接字
工厂”。 第三个构造函数用于激活对象并从指定的 port 将其(用 ActivationID,id)导
出。当对象本身被其专用的“激活”构造函数激活时,Activatable 类的具体子
类必须调用该构造函数。该“激活”构造函数的参数必须是: 对象的激活标识符 (ActivationID) 及
对象的初始化/自举数据 (MarshalledObject)。
作为构造的副效应,远程对象将被“导出”到 RMI 运行时中(从指定 port)并
可接受来自客户机的调用。如果将对象导出到 RMI 运行时失败,则该构造函数将
抛出 RemoteException。 第四个构造函数与第三个构造函数相同,但它允许将客户机和服务器套接字工厂
规范用于与该可激活对象进行通讯。 getID 方法返回对象的激活标识符。该方法是受保护的,因而只有子类才能获取
对象的标识符。 对象的标识符用于报告对象的非活动状态或注销对象的激活描述
符。 register 方法为可激活的远程对象向激活系统注册对象描述符 desc,从而使该
对象能在需要时被激活。该方法可用于注册可激活对象而无需先创建该对象。它
返回可激活对象的 Remote stub,因此可加以保存并在今后调用,从而强制首次
创建/激活该对象。如果没有向激活系统注册 desc 中的组标识符,该方法将抛出
UnknownGroupException。如果激活系统未运行,则抛出 ActivationException
。最后,如果对激活系统的远程调用失败,则抛出 RemoteException。 inactive 方法用于通知系统具有相应激活 id 的对象当前是非活动的。如果已知
该对象当前是活动的,则该对象将不从 RMI 运行时环境中导出(仅在没有待定的
或执行的调用时),从而可不再接收到来的调用。该调用还将通知该虚拟机的 A
ctivationGroup 该对象处于非活动状态;而该组又将通知其 ActivationMonito
r。如果该调用成功,则此后对激活器的激活请求将导致重新激活该对象。如果成
功地阻止该对象被导出(意味着此时它没有待定或执行调用),则 inactive 方
法返回 true;如果由于待定或进程内调用而不能阻止该对象被导出,则返回 fa
lse。如果对象是未知的(它可能是非活动的),则该方法将抛出 UnknownObjec
tException;如果组是非活动的,则抛出 ActivationException;如果通知显示
器的调用失败,则抛出 RemoteException。如果该对象被认为是活动的但已自己
阻止导出,则该操作仍将成功进行。 unregister 方法用于撤消与 id 关联的激活描述符的先前注册。对象不能再用这
个 id 来激活。如果该对象 id 对激活系统来说是未知的,则抛出 UnknownObje
ctException。如果激活系统未处于运行状态,则抛出 ActivationException。如
果对激活系统的远程调用失败,则抛出 RemoteException。 第一个 exportObject 方法可被“可激活”但不扩展 Activatable 类的对象显式
调用,其目的是:a) 向激活系统注册对象的激活描述符 desc,由所提供的 cod
ebase 和 data 构造(因此可激活该对象);b) 从指定 port 导出远程对象 ob
j(如果该 port 为零,则将选择匿名端口)。对象一旦被导出,即可接收到来的
调用。 该 exportObject 方法返回向激活系统注册描述符 desc 时所获得的激活标识符
。如果在虚拟机中激活组不是活动的,则抛出 ActivationException。如果对象
注册或导出失败,则抛出 RemoteException。 如果 obj 扩展 Activatable,则不必调用该方法,因为第一个 Activatable 构
造函数将调用该方法。 第二个 exportObject 方法与第一个相同,但它允许将客户机和服务器套接字工
厂规范用于与该可激活对象进行通讯。 第三个 exportObject 方法将带标识符 id 的“可激活”远程对象(不一定是 A
ctivatable 类型的)导出到 RMI 运行时,以使对象 obj 能够接收到来的调用。
如果 port 为零,则将在某个匿名端口导出该对象。 激活时,不扩展 Activatable 类的“可激活”对象应显式地调用此 exportObje
ct 方法。确实扩展 Activatable 类的对象不必直接调用该方法;它将被上面的
第三个构造函数所调用(应从其专用的激活构造函数中调用子类)。 该 exportObject 方法将返回可激活对象的 Remote stub。如果该对象的导出失
败,则该方法将抛出 RemoteException。 第四个 exportObject 方法与第三个相同,但它允许将客户机和服务器套接字工
厂规范用于与该可激活对象进行通讯。 unexportObject 方法使远程对象 obj 不能接收到来的调用。如果参数 force 为
true,则即使有对远程对象的待定调用或远程对象在进程内仍有调用,对象也将
被强制阻止导出。如果参数 force 为 false,则只在没有对对象的待定调用或进
程内没有调用时它才被阻止导出。如果成功地阻止了该对象的导出,则 RMI 运行
时将从其内部表中删掉它。使用这种强制方式从 RMI 删除对象可能会使客户机持
有远程对象的过时远程引用。如果此前没有将对象导出到 RMI 运行时,则该方法
将抛出 java.rmi.NoSuchObjectException。 构造可激活的远程对象
为了使对象可激活,“可激活”对象实现类不管是否扩展 Activatable 类,都必
须定义一个特殊的公共构造函数。该公共构造函数带有两个参数:激活标识符(
类型为 ActivationID)及激活数据 java.rmi.MarshalledObject(在注册时所用
的激活描述符中提供)。当激活组在其虚拟机内激活某个远程对象时,它将利用
此特殊构造函数来构造对象(后面将作详细说明)。远程对象实现可适当用激活
数据来初始化自己。远程对象也可能想保留其激活标识符,这样就可以在它变为
非活动状态(通过对 Activatable. inactive 方法的调用)时通知激活组。 Activatable 构造函数的第一种和第二种形式用于从指定的 port 注册并导出可
激活对象。最初构造对象时应使用该构造函数;该构造函数的第三种形式将在重
新激活对象时使用。 Activatable 的具体子类必须在最初构造期间调用第一种或第二种构造函数以注
册并导出对象。 构造函数首先用对象的类名创建一个激活描述符 (ActivationD
esc)、对象所提供的 codebase 和 data(其激活组为虚拟机的缺省激活组)。然
后,构造函数将向缺省的 ActivationSystem 注册该描述符。最后,构造函数在
特定 port 上(如果 port 为零,则选择匿名端口)将可激活对象导出到 RMI 运
行时中,并将该对象作为 activeObject 报告给本地 ActivationGroup。如果在
注册或导出的过程中出错,则构造函数抛出 RemoteException。注意,构造函数
也初始化它的 ActivationID(通过注册获得),因此接下来对 protected 方法
getID 的调用将返回对象的激活标识符。 Activatable 构造函数的第三种形式用于从指定的端口导出对象。当 Activatab
le 的具体子类被对象自己的“激活”构造函数激活时,必须调用第三种构造函数
。该“激活”构造函数带两个参数: 对象的 ActivationID
对象的初始化数据,一个 MarshalledObject 对象
该构造函数只将可激活对象从特定 port(如果 port 为零,则选择匿名端口)导
出到 RMI 运行时中,而并不通知 ActivationGroup 该对象已是活动对象。因为
正是 ActivationGroup 激活该对象的,因而它自然知道对象已处活动状态。 下面是远程对象接口 Server 和扩展 Activatable 类的实现 ServerImpl 的示例: package examples; public interface Server extends java.rmi.Remote
{
public void doImportantStuff()
throws java.rmi.RemoteException;
}  public class ServerImpl extends Activatable implements Server
{
// 初始构造、注册及导出时的构造函数
public ServerImpl(String codebase, MarshalledObject data)
throws ActivationException, java.rmi.RemoteException
{
// 向激活系统注册对象,然后
// 在匿名端口上导出
super(codebase, data, false, 0);
} // 激活及导出的构造函数。该构造函数由
// ActivationInstantiator.newInstance
// 方法在激活过程中所调用,以构造对象
public ServerImpl(ActivationID id, MarshalledObject data)
throws java.rmi.RemoteException
{
// 调用父类的构造函数以
// 将对象导出到 RMI 运行时。
super(id, 0);
// 初始化对象(例如利用数据)
} public void doImportantStuff() { ... }
}
对象将负责导出自己。Activatable 的构造函数负责将对象导出到具有活动引用
类型 UnicastRemoteObject 的 RMI 运行时中,因此,扩展 Activatable 的对象
实现不必关心显式导出对象(不是调用相应的超类构造函数)的具体细节。如果
对象实现不扩展类 Activatable,则该对象必须通过调用某个 Activatable.exp
ortObject 静态方法来显式导出该对象。 下例中,ServerImpl 不扩展 Activatable,而是扩展另一个类。因此,ServerI
mpl 负责在初始构造和激活过程中导出自己。下述的类定义给出了 ServerImpl
的初始化构造函数及其特殊“激活”构造函数,以及每个构造函数内用于导出对
象的相应调用: package examples; public class ServerImpl extends SomeClass implements Server
{
// 初始创建时的构造函数
public ServerImpl(String codebase, MarshalledObject data)
throws ActivationException, java.rmi.RemoteException
{
// 注册并导出对象
Activatable.exportObject(this, codebase, data, false, 0);
} // 激活的构造函数
public ServerImpl(ActivationID id, MarshalledObject data)
throws java.rmi.RemoteException
{
// 导出对象
Activatable.exportObject(this, id, 0);
} public void doImportantStuff() { ... }
} 在不创建对象的情况下注册激活描述符
要在不创建对象的情况下向激活系统注册可激活远程对象,程序员只需为对象注
册激活描述符(类 ActivationDesc 的实例)。激活描述符含有一切所需的信息
,因此必要时激活系统就可以激活该对象。可用以下方法来注册类 examples.Se
rverImpl 实例的激活描述符(忽略异常处理): Server server;ActivationDesc desc;String codebase = "http://zaphod/cod
ebase/"; MarshalledObject data = new MarshalledObject("some data");desc = new A
ctivationDesc( "examples.ServerImpl", codebase, data);server = (Server
)Activatable.register(desc); register 调用将返回一个 Remote stub(它是 examples.ServerImpl 对象的 s
tub),并实现一组与 examples.ServerImpl 所实现的远程接口相同的远程接口
(即 stub 实现远程接口 Server)。该 stub 对象(上述被强制类型转换并指派
给 server 的对象)可作为参数传给期望实现 examples.Server 远程接口的对象
的任何方法调用。
7.4 激活接口
在 RMI 激活协议中,为使系统运转正常,激活器必须保证以下两点: 象所有的系统守护进程一样,计算机处于打开状态时激活器必须保持运行状态
激活器不能重新激活已处在活动状态的远程对象。
激活器将为它所要激活的组和对象维护一个相应的信息数据库。 7.4.1 激活器接口
激活器是参与激活进程的实体之一。如前所述,stub 内的不完善引用将通过调用
激活器的 activate 方法来获得对可激活远程对象的“活”引用。激活器接到激
活请求后,就开始查找该激活标识符 id 的激活描述符,以决定要在哪个组中激
活对象,然后调用激活组的实例化器的newInstance 方法(ActivationGroup 的
远程接口将在后面说明)。激活器将根据需要启动激活组的执行过程。例如,如
果给定的组描述符的激活组还未运行,则激活器将为该激活组生成一个子虚拟机
,以便在该新虚拟机中建立该组。 激活器将负责监控和检测激活组何时失败,以便将过时的远程引用从其内部表中
删去。 package java.rmi.activation; public interface Activator extends java.rmi.Remote
{
java.rmi.MarshalledObject activate(ActivationID id, boolean force)
throws UnknownObjectException, ActivationException,
java.rmi.RemoteException;
}
activate 方法激活与激活标识符 id 所关联的对象。如果激活器知道对象已经为
活动对象,且 force 参数为 false,就会立即将含有“活”引用的 stub 返给调
用程序;如果激活器不知道相应的远程对象是活动的或 force 参数为 true,则
激活器将利用激活描述符信息(先前已为获得 id 而注册过)来决定对象应该在
哪个组(虚拟机)中被激活。如果对象组的相应激活 Instantiator 已经存在,
则激活器将通过传递 id 和对象的激活描述符来调用该激活实例化器的newInsta
nce 方法。 如果对象组描述符的激活实例化器(组)还不存在,则激活器将启动新的 Activ
ationInstantiator 化身执行进程(例如通过生成子进程)。当激活器为某一组
重建 ActivationInstantiator 时,它必须将该组的化身数增一。注意,化身数
从 0 开始。激活系统用化身数来检测后来的 ActivationSystem.activeGroup 和
ActivationMonitor.inactiveGroup 调用。激活系统将弃去那些化身数小于组的
当前数的调用。
----------------------------------------------------------------------
---------- 注意 - 激活器在启动新的激活组时必须与激活组的标识符、描述符及化身数进行
通讯。激活器将在一个独立的虚拟机中(例如作为一个独立的进程或子进程)生
成激活组。因此,它必须将信息传递过去,以指定用 ActivationGroup.createG
roup 方法来创建该组时所需的信息。激活器并未指定如何将此信息传给所生成的
进程。可用已编组对象的形式将此信息送到子进程的标准输入中。
----------------------------------------------------------------------
----------
当激活器接收到激活组的回调(通过 ActivationSystem.activeGroup 方法,该
回调可指明激活组的引用和化身数)时,激活器就可以调用该激活实例化器的 n
ewInstance 方法,从而将每个待定的激活请求转发给该激活实例化器,同时将结
果(一个已编组远程对象引用、一个 stub)返给每个调用程序。 注意,激活器接收的是 MarshalledObject 对象而不是 Remote 对象。因此,激
活器不需要加载该对象的代码,也不需要参与该对象的分布式垃圾收集。如果激
活器保留了对远程对象的强引用,则该激活器将防止该对象在正常的分布式垃圾
收集机制下被当作垃圾而收集。 如果激活失败,activate 方法将抛出 ActivationException。激活可能会因以下
各种原因而失败:找不到类、无法与激活组取得联系等。如果先前没有为激活标
识符 id 向该激活器注册激活描述符,则 activate 方法将抛出 UnknownObject
Exception。如果对该激活器的远程调用失败,则抛出 RemoteException。 7.4.2 ActivationSystem 接口
ActivationSystem 提供了一种为组和可激活对象(这些对象将在这些组中被激活
)注册的方法。 ActivationSystem 与 Activator 和 ActivationMonitor 都能
密切合作。前者负责激活通过 ActivationSystem 所注册的对象;后者负责获取
活动对象、非活动对象及非活动组的有关信息。 package java.rmi.activation; public interface ActivationSystem extends java.rmi.Remote
{
public static final int SYSTEM_PORT = 1098; ActivationGroupID registerGroup(ActivationGroupDesc desc)
throws ActivationException, java.rmi.RemoteException; ActivationMonitor activeGroup(ActivationGroupID id,
ActivationInstantiator group,
long incarnation)
throws UnknownGroupException, ActivationException,
java.rmi.RemoteException; void unregisterGroup(ActivationGroupID id)
throws ActivationException, UnknownGroupException,
java.rmi.RemoteException; ActivationID registerObject(ActivationDesc desc)
throws ActivationException, UnknownGroupException,
java.rmi.RemoteException; void unregisterObject(ActivationID id)
throws ActivationException, UnknownObjectException,
java.rmi.RemoteException; void shutdown() throws java.rmi.RemoteException;
}  ----------------------------------------------------------------------
---------- 注意 - 作为一种安全措施,以上所有方法(registerGroup、activeGroup、unr
egisterGroup、registerObject、unregisterObject 和 shutdown)如果被客户
机所调用,且该客户机所处主机与激活系统不同,则将抛出 java.rmi.AccessEx
ception。该异常是 java.rmi.RemoteException 的子类。
----------------------------------------------------------------------
---------- registerObject 方法用来注册激活描述符 desc,同时也为可激活远程对象获取
激活标识符。 ActivationSystem 为描述符 desc 所指定的对象创建 Activatio
nID(激活标识符),并将激活描述符及其关联的标识符记录在稳定的存储库中以
备将来使用。当 Activator 接到某个特定标识符的 activate 请求时,它就查找
该指定标识符的激活描述符(先前已注册),并用此信息来激活该对象。如果 d
esc 中所引用的组并没有向该系统注册,则该方法将抛出 UnknownGroupExcepti
on。如果注册失败(例如,数据库更新失败等),则抛出 ActivationException
。如果远程调用失败, 则抛出 RemoteException。 unregisterObject 方法用于删除激活标识符 id 及以前向 ActivationSystem 注
册过的相关描述符。该调用完成后,就不能再用对象的激活 id 来激活该对象。
如果对象的 id 是未知的(没有被注册),则该方法将抛出 UnknownObjectExce
ption。如果注销失败(例如,数据库的更新失败等),则该方法将抛出 Activa
tionException。如果远程调用失败,则抛出 RemoteException。 registerGroup 方法用于向激活系统注册组描述符 desc 所指定的激活组,并返
回指派给该组的 ActivationGroupID。必须先向 ActivationSystem 注册激活组
,然后才能在该组中注册对象。 如果组的注册失败,则该方法将抛出 Activati
onException。如果远程调用失败,则抛出 RemoteException。 activeGroup 方法是来自 ActivationGroup(具有标识符 id)的回调,用以通知
激活系统下列信息:组现在是活动的,而且也是该虚拟机的 ActivationInstant
iator。此回调是在 ActivationGroup.createGroup 方法内部进行的,可以获取
ActivationMonitor。组将用该 ActivationMonitor 就对象和组的状态(即该组
内的组和对象已变为非活动的)对系统进行更新。 如果该组没有被注册,则抛出
UnknownGroupException。如果该组已是活动的,则抛出 ActivationException
。如果对激活系统的远程调用失败,则抛出 RemoteException。 unregisterGroup 方法将具有标识符 id 的激活组从激活系统中删掉。激活组将
进行此回调,以通知激活器应该将该组销毁。如果此调用顺利完成,就将无法再
在该组内注册和激活对象。有关该组及其相关对象的所有信息都将从系统中删掉
。如果该组是未经注册的,则该方法将抛出 UnknownGroupException。如果远程
调用失败,则抛出 RemoteException。如果注销失败(如数据库更新失败等),
则抛出 ActivationException。 shutdown 方法将以适当的异步方式终止激活系统和所有有关的激活进程(激活器
、监控器及组)。由激活守护进程所生成的所有组都将被销毁,激活守护进程也
将退出。要关闭激活系统守护进程 rmid,请执行命令: rmid -stop [-端口号] 该命令将关闭指定端口上的激活守护进程(如果没有指定端口,则将关闭缺省端
口上的守护进程)。
  7.4.3 ActivationMonitor 类
ActivationMonitor 是 ActivationGroup 特有的,它可通过调用 ActivationSy
stem.activeGroup 以报告某个组时获得(这种调用是在 ActivationGroup.crea
teGroup 方法内部进行的)。激活组在出现以下情况时负责通知 ActivationMon
itor:它的对象已变成活动对象、非活动对象或该组已整个变成非活动。 package java.rmi.activation;
public interface ActivationMonitor extends java.rmi.Remote
{ public abstract void inactiveObject(ActivationID id)
throws UnknownObjectException, RemoteException; public void activeObject(ActivationID id,
java.rmi.MarshalledObject mobj)
throws UnknownObjectException, java.rmi.RemoteException; public void inactiveGroup(ActivationGroupID id, long incarnation)
throws UnknownGroupException, java.rmi.RemoteException;
}
当激活组中的某个对象变成非活动(停用)时,该激活组将调用其监控器的 ina
ctiveObject 方法。激活组通过调用它的 inactiveObject 方法来发现其虚拟机
中的对象(该对象参与了激活)是否已为非活动状态。 inactiveObject 调用将通知 ActivationMonitor 其所保留的、具有激活标识符
id 的对象的远程对象引用已不再有效。监控器将把与 id 关联的引用视为已过
时。由于该引用被当成过时引用,因此后面为同一激活标识符所进行的 activat
e 调用将导致远程对象被重新激活。如果该对象对 ActivationMonitor 未知,该
方法将抛出 UnknownObjectException。如果远程调用失败,则抛出 RemoteExce
ption。 activeObject 调用通知 ActivationMonitor 与 id 相关联的对象现在是活动的
。参数 obj 是该对象的 stub 的编组表述。 如果组中的某个对象是通过其它方
法被激活的,而不是由系统直接激活的(也就是说,对象被注册并自己“激活”
自己),则 ActivationGroup 必须通知其监控器。如果该对象的标识符先前没有
被注册,则该方法将抛出 UnknownObjectException。如果远程调用失败,则抛出
RemoteException。 inactiveGroup 调用通知监控器由 id 和 incarnation 所指定的组现在已非活动
。一旦下一个要求在该组内激活对象的请求出现时,将用一个更大的化身数来重
新创建该组。当组中所有对象都报告说它们已非活动对象时,该组即变成非活动
组。如果该组的 id 没有被注册,或者化身数比该组当前的化身数更小,则该方
法将抛出 UnknownGroupException。如果远程调用失败,则抛出 RemoteExcepti
on。  
7.4.4 ActivationInstantiator 类
ActivationInstantiator 负责创建可激活对象的实例。ActivationGroup 的具体
子类实现 newInstance 方法,以便对在组内创建对象的过程进行控制。
package java.rmi.activation; public interface ActivationInstantiator extends java.rmi.Remote
{
public MarshalledObject newInstance(ActivationID id,
ActivationDesc desc)
throws ActivationException, java.rmi.RemoteException;
}
为了在该组内重新创建具有激活标识符 id、描述符 desc 的对象,激活器需要调
用实例化器的 newInstance 方法。实例化器负责: 用描述符的 getClassName 方法来决定对象的类;
从由描述符中所获得的 cadebase 路径中加载对象的类(用 getLocation 方法)

通过调用对象的类的特殊“激活”构造函数创建该类的实例。这种特殊构造函数
带有两个参数:一个是对象的 ActivationID,另一个是含有对象特有的初始化数
据的 MarshalledObject;
返回一个含有它所创建的远程对象的 MarshalledObject。
实例化器还负责在它所创建或激活的对象不再是活动对象时进行报告,因此可以
对其 ActivationMonitor 执行相应的 inactiveObject 调用(详细信息,参见
ActivationGroup 类)。 如果对象的激活失败, newInstance 方法将抛出 ActivationException。如果远
程调用失败,则该方法将抛出 RemoteException。 7.4.5 ActivationGroupDesc 类
激活组描述符 ActivationGroupDesc 含有创建或重新创建激活组(将在该激活组
中激活相同 Java 虚拟机上的对象)所需的信息。 这种描述符含有: 组的类名,
组的 cadebase 路径(组的类所在位置),
“已编组”对象,它可包含对象特定的初始化数据。
对象的类必须是 ActivationGroup 的具体子类。ActivationGroup 的子类是用
ActivationGroup.createGroup 静态方法来创建或重新创建的,该静态方法将调
用含有以下两个参数的特殊构造函数: 该组的 ActivationGroupID,
该组的初始化数据(在 java.rmi.MarshalledObject 中) package java.rmi.activation; public final class ActivationGroupDesc implements java.io.Serializable
{ public ActivationGroupDesc(java.util.Properties props,
CommandEnvironment env);; public ActivationGroupDesc(String className,
String codebase,
java.rmi.MarshalledObject data,
java.util.Properties props,
CommandEnvironment env); public String getClassName(); public String getLocation(); public java.rmi.MarshalledObject getData(); public CommandEnvironment getCommandEnvironment(); public java.util.Properties getPropertiesOverrides(); }
第一个构造函数将创建一个组描述符,它用系统的缺省值来作为组的实现和代码
位置。各属性指定了 Java 环境覆盖(它将覆盖组实现虚拟机中的系统属性)。
命令环境可控制启动子虚拟机时所用的确切命令/选项,也可为 null 以接受 rm
id 的缺省值。 第二个构造函数和第一个相同,但它允许指定 Properties 和 CommandEnvironm
ent。 getClassName 方法返回组的类名。 getLocation 方法返回 cadebase 路径,从中可加载该组的类。 getData 方法以编组形式返回组的初始化数据。 getCommandEnvironment 方法返回命令环境(可能为 null)。 getPropertiesOverrides 方法返回该描述符的属性覆盖(可能为 null)。   7.4.6 ActivationGroupDesc.CommandEnvironment 类
CommandEnvironment 类允许对缺省系统属性进行覆盖,并可为 ActivationGrou
p 指定由实现所定义的选项。 public static class CommandEnvironment implements java.io.Serializable
{
public CommandEnvironment(String cmdpath, String[] args);
public boolean equals(java.lang.Object);
public String[] getCommandOptions();
public String getCommandPath();
public int hashCode();
}
  构造函数将用所给的命令 cmdpath 和另一个命令行选项 args 创建 CommandEnv
ironment。 equals 实现对命令环境对象的内容等效操作。对 hashCode 方法进行适当的实现
可在必要时将 CommandEnvironment 对象储存在散列表中。 getCommandOptions 方法返回环境对象的命令行选项。 getCommandPath 方法返回环境对象的命令字符串。 7.4.7 ActivationGroupID 类
注册过的激活组的标识符可有以下几个用途: 在激活系统中对该组进行唯一标识
含有对该组的激活系统的引用,因而必要时该组能与其激活系统联系。
ActivationGroupID 将在调用 ActivationSystem.registerGroup 时被返回,并
用来在该激活系统内标识该组。当创建或重新创建激活组时,该组标识符将被作
为参数传给激活组的特殊构造函数。
package java.rmi.activation; public class ActivationGroupID implements java.io.Serializable
{
public ActivationGroupID(ActivationSystem system); public ActivationSystem getSystem(); public boolean equals(Object obj); public int hashCode();
} ActivationGroupID 构造函数创建唯一的组标识符。该标识符的 ActivationSys
tem 是 system。 getSystem 方法返回组的激活系统。 hashCode 方法返回组标识符的散列码。两个指向同一远程组的组标识符将具有相
同的散列码。 equals 方法比较两个组标识符内容的等效性。如果以下两个条件均成立,该方法
将返回 true: 1) 两个唯一标识符在内容上等价。2) 各标识符中指定的激活系
统都引用同一远程对象。   7.4.8 ActivationGroup 类
ActivationGroup 负责在其组中创建“可激活”对象的新实例,并在出现下列情
况时通知其 ActivationMonitor: 其对象变成活动或非活动,或者该组整体变成
非活动。 ActivationGroup 最初是由以下方法之一创建的: 作为创建对象的“缺省” ActivationDesc 的副效应而创建
通过对 ActivationGroup.createGroup 方法的显式调用而创建
作为在组(该组中仅 ActivationGroupDesc 被注册过)中激活第一个对象时的副
效应而创建。
只有激活器才能重新创建 ActivationGroup。必要时,激活器将为每个已注册的
激活组生成一个独立的虚拟机(例如作为子进程)并将激活请求定向到相应的组
。生成虚拟机的方式与实现有关。激活组是通过 ActivationGroup.createGroup
静态方法来创建的。createGroup 方法对即将创建的组有两个要求: 1) 该组必
须是 ActivationGroup 的具体子类;2) 该组必须有一个取以下两个参数的构造
函数: 该组的 ActivationGroupID,
该组的初始化数据(包含在 MarshalledObject中)
创建完毕,ActivationGroup 的缺省实现将在创建该组的 ActivationGroupDesc
时把系统属性设置为强制系统属性,并将安全管理器设置为 java.rmi.RMISecu
rityManager。如果应用程序要求当对象在组中被激活时设置某些特定的属性,则
该应用程序应在创建 ActivationDesc 之前(缺省 ActivationGroupDesc 创建前
)设置这些属性。 package java.rmi.activation; public abstract class ActivationGroup
extends UnicastRemoteObject
implements ActivationInstantiator
{
protected ActivationGroup(ActivationGroupID groupID)
throws java.rmi.RemoteException; public abstract MarshalledObject newInstance(ActivationID id,
ActivationDesc desc)
throws ActivationException, java.rmi.RemoteException; public abstract boolean inactiveObject(ActivationID id)
throws ActivationException, UnknownObjectException,
java.rmi.RemoteException; public static ActivationGroup createGroup(ActivationGroupID id,
ActivationGroupDesc desc,
long incarnation)
throws ActivationException; public static ActivationGroupID currentGroupID(); public static void setSystem(ActivationSystem system)
throws ActivationException; public static ActivationSystem getSystem()
throws ActivationException; protected void activeObject(ActivationID id,
java.rmi.MarshalledObject mobj)
throws ActivationException, UnknownObjectException,
java.rmi.RemoteException; protected void inactiveGroup()
throws UnknownGroupException, java.rmi.RemoteException;
}
激活器通过调用激活组的 newInstance 方法来激活具有激活描述符 desc 的对象
。激活组负责: 利用描述符的 getClassName 方法来确定该对象的类;
通过 getLocation 方法从由描述符中所得到的 URL 路径中加载该类;
通过调用该对象类的特殊构造函数来创建该类的实例。该构造函数取以下两个变
量:对象的 ActivationID 和含有对象初始化数据的 MarshalledObject;
将刚创建的远程对象的序列化版本返给激活器。
如果无法创建所给描述符的实例,该方法将抛出 ActivationException。 对该组的 inactiveObject 方法的调用是通过调用 Activatable.inactive 方法
来间接进行的。当远程对象停用时(该对象被认为非活动的),该对象的实现必
须调用 Activatable 的 inactive。如果对象在非活动时不调用 Activatable.i
nactive,则该对象将永远不会被作为垃圾收集,因为组将对其所创建的对象保留
强引用。 组的 inactiveObject 方法可阻止从 RMI 运行时中导出与 id 关联的远程对象(
仅当没有对该对象的待定或正在执行的调用时)。使该对象将无法再接收到来的
RMI 调用。如果该对象当前还有待定或正在执行的调用,inactiveObject 将返
回 false 且不采取任何动作。 如果 unexportObject 操作成功,意味着该对象没有待定或正在执行的调用。此
时组将通过监控器的 inactiveObject 方法通知它的 ActivationMonitor 该远程
对象当前是非活动的,以便激活器在接到下一激活请求时即重新将该远程对象激
活。如果操作成功,inactiveObject 将返回 true。如果该对象被 ActivationG
roup 认为是活动的但已被阻止导出,操作将仍有可能成功。 如果激活组不知道该对象(例如该对象先前已被报告为是非活动的,或者从未通
过该激活组来激活该对象),则 inactiveObject 方法将抛出 UnknownObjectEx
ception。如果非活动操作失败(例如,对激活器或激活组的远程调用失败),则
将抛出 RemoteException。 createGroup 方法将为当前虚拟机创建和设置激活组。只有在激活组是当前未设
定的情况下才能对它进行设置。当 Activator 为了执行到来的 activate 请求而
启动某一激活组的重新创建过程时,该激活组即通过 createGroup 方法进行设置
。组必须先向 ActivationSystem 注册一个描述符,然后才能用该方法(将注册
中得到的 ActivationID 传给它)来创建该组。 由 ActivationGroupDesc(即 desc)指定的组必须是 ActivationGroup 的具体
子类并具有公共构造函数。该公共构造函数取两个参数:一个是组的 Activatio
nGroupID;另一个是 MarshalledObject,它含有组的初始化数据(从组的Activ
ationGroupDesc 中获得的)。注意: 如果应用程序要创建自己的自定义激活组
,该组必须在构造函数中设置安全管理器。否则将不能在该组中激活对象。 创建完组后,即通知 ActivationSystem 该组已通过调用 activeGroup 方法(该
方法将返回组的 ActivationMonitor)而被激活。应用程序不必单独调用 activ
eGroup,因为 createGroup 方法将负责这一回调。 一旦组被创建,对 currentGroupID 方法的后续调用都将返回该组的标识符,直
到该组变成非活动组为止。那时,currentGroupID 方法将返回 null。 参数 incarnation 表示当前的组化身数,也就是该组被激活的次数。一旦组被成
功创建,化身数就被用作 activeGroup 方法的参数。化身数从 0 开始。如果该
组已经存在,或在组的创建过程中出错,createGroup 方法将抛出 ActivationE
xception。 setSystem 方法用于设置虚拟机的 ActivationSystem (system)。只有当前没有
组为活动组时才能对激活系统进行设置。如果没有通过显式调用 setSystem 来设
置激活系统,则 getSystem 方法将尝试通过在 Activator 的注册服务程序中查
找名称 java.rmi.activation.ActivationSystem 来获得对 ActivationSystem
的引用。缺省情况下,查找激活系统所用的端口号将由 ActivationSystem.SYST
EM_PORT 定义。通过设置属性 java.rmi.activation.port 可将该端口覆盖掉。
如果调用 setSystem 时激活系统已经设置好,则该方法将抛出 ActivationExce
ption。 getSystem 方法返回该虚拟机的激活系统。激活系统可由 setSystem 方法(前述
)设置。 activeObject 方法是一个受保护的方法。子类将用它来对组监控器进行 active
Object 回调,以通知监控器具有指定激活 id 且其 stub 包含在 mobj 中的远程
对象现在是活动的。这一调用只是转发给组的 ActivationMonitor。 inactiveGroup 方法是一个受保护的方法。子类用它来通知组监控器该组已变成
非活动的。当组中每个参与虚拟机中激活活动的对象都变成非活动时,子类就进
行此调用。 7.4.9 MarshalledObject 类
MarshalledObject 是某个对象的容器,它允许在进行 RMI 调用时将该对象当作
一个参数来传递,但它延迟该对象在接收端上的序列化恢复,直到应用程序明确
地请求该对象(通过调用该容器对象)。包含在该 MarshalledObject 中的可序
列化对象是用相同的语义(该语义作为 RMI 调用中的参数来传递)来序列化和序
列化恢复的(请求时)。也就是说,MarshalledObject 中的任一远程对象都可表
示为其 stub 的序列化实例。MarshalledObject 所包含的对象可以是远程对象、
非远程对象或远程对象和非远程对象的完整图形。 当对象被放入 MarshalledObject 的 wrapper 中时,该对象的序列化形式将被注
释为 cadebase URL(可从中加载类);同样,当从对象的 MarshalledObject
的 wrapper 中取回该对象时,如果本地没有该对象的代码,则使用该 URL(序列
化时所注解的)来定位和加载该对象类的字节代码。 package java.rmi; public final class MarshalledObject implements java.io.Serializable
{
public MarshalledObject(Object obj) throws java.io.IOException; public Object get()
throws java.io.IOException, ClassNotFoundException; public int hashCode(); public boolean equals();
}
MarshalledObject 的构造函数将可序列化的对象 obj 作为单一参数,并将对象
的编组表示存放在字节流中。对象的编组表示保存了对象在 RMI 调用时所传递的
语义: 流中的每个类都被注解为其 cadebase URL,以便在重构该对象(通过调用 get
方法)时可以找到各个类的字节代码并进行加载;
远程对象由它们的代理服务器 stub 所代替。
当把类 MarshalledObject 的实例写到 java.io.ObjectOutputStream 中时,所
含对象的编组形式(在构造中创建)亦被写到流中。因此,只有字节流才被序列
化。 当从 java.io.ObjectInputStream 中读取 MarshalledObject 时,并不把所包含
的对象序列化恢复为一个具体对象;该对象将一直保持其编组表示,直到该已编
组对象的 get 方法被调用为止。 get 方法总是要用所含对象的编组形式来重新构造该对象。内部表示将用解编参
数(用于 RMI 调用)所用的语义来进行序列化恢复。因此,对象表示的序列化恢
复将用嵌入对象序列化流中的 URL 注解来加载类的代码(如果本地没有该代码)
。 对象编组表示的 hashCode 与传递给构造函数的对象相同。如果进行比较的对象
的编组表示是等价的,则 equals 方法将返回 true。该 equals 方法所采用的比
较将忽略类的 cadebase 注解,即除了序列化表示中各个类的 cadebase 外,只
要两个对象具有相同的序列化表示,则这两个对象就是等价的。