Dict.CN 在线词典, 英语学习, 在线翻译

都市淘沙者

荔枝FM Everyone can be host

统计

留言簿(23)

积分与排名

优秀学习网站

友情连接

阅读排行榜

评论排行榜

java中传输对象时候的问题(转)

对于同一个socket,如果调用两次就会抛出StreamCorruptedException

如果你使用socket,并通过对象输入/输出流来处理的话,并且已经对某个socket调用了一次getInputStream时,但又需要把这个socket的相关信息作为参数传递给别的对象时,应注意:不用直接把socket传过去,应该把对应的ObjectInputStream或ObjectOutputStream对象传递过去。

调用getInputStream方法就会读取标示头信息。用缺省的serializetion的实现时,一个ObjectOutputStream的构造和一个ObjectInputStream的构造必须一一对应.ObjectOutputStream的构造函数会向输出流中写入一个标识头,而ObjectInputStream会首先读入这个标识头.因此,多次以追加方式向一个文件中写入object时,该文件将会包含多个标识头.所以用ObjectInputStream来deserialize这个ObjectOutputStream时,将产生StreamCorruptedException.


==============================

使用ObjectStream会出现的问题

1. ObjectInputStream与ObjectOutputStream的顺序问题
在网络通讯中,主机与客户端若使用ObjectInputStream与ObjectOutputStream建立对象通讯,必须注意声明此两个对象的顺序。
如:
主机端先建立ObjectInputStream后建立ObjectOutputStream,则对应地客户端要先建立ObjectOutputStream后建立ObjectInputStream,否则会造成两方互相等待数据而导致死锁。
原因是建立ObjectInputStream对象是需要先接收一定的header数据,接收到这些数据之前会处于阻塞状态。以下为JAVA API文档的说明
Creates an ObjectInputStream that reads from the specified InputStream.
A serialization stream header is read from the stream and verified.
This constructor will block until the corresponding ObjectOutputStream
has written and flushed the header.

故而为了防止这种死锁状态,通讯两方的ObjectInputStraem,ObjectOutputStream必须注意顺序对应使用。

2. ObjectInputStream接收到非ObjectOutputStream数据的问题
在使用ObjectInputStream与ObjectOutputStream对象通讯的通讯双方,假设客户端程序出现错误,发送了非ObjectOutputStream封装发送的数据(比如发送一个数字或字符串到主机),则主机端的ObjectInputStream接收到错误数据后不能自动纠正,会一直接收数据而处于阻塞状态,从而导致通讯失败。尚未找到解决方法。目前想的办法为写自己的ObjectStream类。

3. 解决版本问题
使用ObjectStream的时候会额外发送一个关于对象的序列号
static final long serialVersionUID = ....
手动加入此域则可避免版本差异导致的问题。
对象序列号的计算可用SDK的serialver计算。

======================================

ObjectInputStream ObjectOutputStream

ObjectOutputStream和ObjectInputStream

--ObjectOutputStream

ObjectInputStream 类恢复以前使用 ObjectOutputStream 类序列化后的基本类型数据和对象。

ObjectOutputStream 和 ObjectInputStream 分别利用 FileOutputStream 和 FileInputStream 能支持应用程序实现对象图象的稳定存储。

ObjectInputStream 可用于恢复以前序列化过的对象。另外其它一些情况也使用此类,诸如使用一个 Socket 在主机间传递对象时,

或在远程通讯系统中为实现参数和参变量的通讯而进行对象传递时。

ObjectInputStream 保证从流中创建的图象中的所有对象的类型与 Java 虚拟机中出现的类匹配。使用标准机制按需装载相应类。

只有支持 java.io.Serializable 或 java.io.Externalizable 接口的对象才能从流中读取。使用 readObject 方法从该流中

读取一个对象。 Java 的安全造型应该用于获取期望类型。在 Java 中, 串和数组都是对象且可当作是序列化过程中的对象。

读取时,它们需要转换为所需类型。

另外基类型也可使用 DataInput 中的正确方法从该流中读取。

对象的缺省逆序列化机制将每个域的内容恢复为它被写入时的值和类型。逆序列化过程中忽略申明为暂时的或静态的域。

对其它对象的引用促使那些对象必须从流中读取。使用引用共享机制正确地恢复对象的图象。逆序列化时总是分配新对象,

防止重写已存在的对象。

读取一个对象同运行一个新对象的构造子类似。为该对象分配的内存初始化为空(NULL)。为非序列化类调用无参构造子,

然后将序列化类的域从该流中恢复,恢复从最接近 java.lang.object 的序列化对象开始,到指定对象结束。

例如读取在示例中写入 ObjectOutputStream 中的流:


FileInputStream istream = new FileInputStream("t.tmp");
ObjectInputStream p = new ObjectInputStream(istream);
int i = p.readInt();
String today = (String)p.readObject();
Date date = (Date)p.readObject();
istream.close();

类通过实现 java.io.Serializable 或 java.io.Externalizable 接口来控制它们的序列化。

实现序列化接口可以使对象能保存和恢复它的完整状态,可以使类在写入流和从流中读取的期间内进行改进。

它自动地遍历对象间的引用,保存和恢复完整图象。在序列化和逆序列化处理过程中需要特定句柄的可序列化类,

必须实现如下这两个方法:


private void writeObject(java.io.ObjectOutputStream stream)
     throws IOException;
private void readObject(java.io.ObjectInputStream stream)
     throws IOException, ClassNotFoundException;

利用 writeObjectmethod 方法将一个特殊类的对象的状态写入某流后,相应的 readObject 方法将负责读取和恢复这些数据。

此方法不必关心状态是属于它的父类还是子类。 从 ObjectInputStream 读取数据恢复单个域的状态,并将之赋给该对象的恰当域。

使用 DataInput 方法读取基本数据类型。

序列化操作对没有实现 java.io.Serializable 接口的对象,不读取或分配它的域值。非序列化对象的子类可以是序列化的。

在这种情况下,非序列化类必须有一个无参构造子,使它的域能使用此构造子完成初始化。 在此情况下,

子类负责保存和恢复非序列化类的状态。通常情况父类的域是可存储的(公有的、包或保护的),

或存在用于恢复它的状态的可使用的获取或设置方法。

ObjectInputStream 能获取逆序列化一个对象期间出现的任一异常,一旦出现异常,则放弃读过程。

实现外部接口可以使对象完全控制此对象序列化形式的内容和格式。

调用外部接口的方法:writeExternal 和 readExternal 保存和恢复对象状态。当一个类实现了这些方法时,

它们就能使用 ObjectOutput 和 ObjectInput 方法的所有方法写入或读取它们自己的状态。对象负责管理它出现的相应版本。


ObjectOutputStream

public class ObjectOutputStream
extends OutputStream
implements ObjectOutput, ObjectStreamConstants
类 ObjectOutputStream 将 Java 对象中的基本数据类型和图元写入到一个 OutputStream 对象中。可使用 ObjectInputStream 读取这些对象。

另外使用此流对应的文件能存储这些对象。如果该流是一个网络通讯流,则在另一台主机或另一个处理机上可重建这些对象。

只有支持 java.io.Serializable 接口的对象才能被写入该流。对每个可序列化的对象进行编码,包括相应类的名称和标记,

对象的属性和数组值,以及初始化对象时引用的任何其它对象等。

使用 writeObject 将一个对象写入该流。任一对象,包括串和数组,均采用 writeObject 方法被写入。

也能将多个对象或基类型对象写入此流。反过来,必须以这些对象被写入的相同类型和相同顺序,

从相应的 ObjectInputstream 流中读回这些对象。

基类型也可使用 DataOutput 中的正确方法写入此流。串对象也可使用 writeUTF 方法写入。

一个对象的缺省序列化机制将写入对象的类,类标记和所有的非暂时的和非静态的属性值。

其它对象(除暂时的或静态的属性)的引用也将促使以上这些对象被写入。 使用共享机制,对单一对象的多次引用进行编码,

以至对象的图元能被存储为与它原来写入时有相同的形状。

例如写入一个对象,此对象能从 ObjectInputStream 中读出:


FileOutputStream ostream = new FileOutputStream("t.tmp");
ObjectOutputStream p = new ObjectOutputStream(ostream);
p.writeInt(12345);
p.writeObject("Today");
p.writeObject(new Date());
p.flush();
ostream.close();

在序列化处理过程中需要特定句柄的类,必须使用如下这些恰当的标记实现特定的方法:

private void readObject(java.io.ObjectInputStream stream)
     throws IOException, ClassNotFoundException;
private void writeObject(java.io.ObjectOutputStream stream)
     throws IOException

writeObject 方法负责写特定类的对象的状态,以使相应的 readObject 方法能存储它。

此方法不必关心写入对象的父类或子类的状态。使用 writeObject 方法或基本类型支持的 DataOutput

方法将每个域的状态保存到 ObjectOutputStream 中。

序列化操作不能输出没有实现 java.io.Serializable 接口的任一对象的域。非序列化对象的子类可以是序列化的。

在这种情况下,非序列化类必须有一个无参构造子,使它的域能被初始化。 在此情况下,子类负责保存和恢复非序列化类的状态。

通常情况父类的域是可存储的(公有的、包或保护的),或存在用于恢复它的状态的可使用的获取或设置方法。

实现抛出 NotSerializableException 异常的 writeObject 和 readObject 方法能阻止一个对象的序列化。

ObjectOutputStream 将获取这个异常,并放弃这个序列化过程。实现外部接口可以使对象完全控制此对象序列化形式的内容和格式。

调用外部接口的方法:writeExternal 和 readExternal 保存和恢复对象状态。当一个类实现了这些方法时,

它们就能使用 ObjectOutput 和 ObjectInput 方法的所有方法写入或读取它们自己的状态。对象负责管理它出现的相应版本。


import java.io.*;
import java.util.*;

public class Logon implements Serializable {

       private Date date = new Date();
       private String username;
       private transient String password;


       Logon(String name, String pwd) {
              username = name;
              password = pwd;
       }


       public String toString() {
              String pwd = (password == null) ? "(n/a)" : password;
              return "logon info: \n " + "username: " + username + "\n date: " + date + "\n password: " + pwd;
       }


       public static void main(String[] args) throws IOException, ClassNotFoundException {
              Logon a = new Logon("Morgan", "morgan83");
              System.out.println( "logon a = " + a);
              ObjectOutputStream o = new ObjectOutputStream( new FileOutputStream("Logon.out"));
              o.writeObject(a);
              o.close();

              int seconds = 5;
              long t = System.currentTimeMillis() + seconds * 1000;
              while(System.currentTimeMillis() < t) ;

              ObjectInputStream in = new ObjectInputStream( new FileInputStream("Logon.out"));
              System.out.println( "Recovering object at " + new Date());
              a = (Logon)in.readObject();
              System.out.println("logon a = " + a);
       }
}

类Logon是一个记录登录信息的类,包括用户名和密码。首先它实现了接口Serializable,这就标志着它可以被序列化。

之后再main方法里ObjectOutputStream o = new ObjectOutputStream( new FileOutputStream("Logon.out"));

新建一个对象输出流包装一个文件流,表示对象序列化的目的地是文件Logon.out。然后用方法writeObject开始写入。

想要还原的时候也很简单ObjectInputStream in = new ObjectInputStream( new FileInputStream("Logon.out"));

新建一个对象输入流以文件流Logon.out为参数,之后调用readObject方法就可以了。

posted on 2011-05-17 08:39 都市淘沙者 阅读(1885) 评论(0)  编辑  收藏 所属分类: Java Basic/Lucene/开源资料


只有注册用户登录后才能发表评论。


网站导航: