`
足至迹留
  • 浏览: 496932 次
  • 性别: Icon_minigender_1
  • 来自: OnePiece
社区版块
存档分类
最新评论

<4> Object Serialization和ObjectOutputStream/ObjectInputStream

阅读更多
[续...]
九、Object Serialization
前面章节介绍的都是如何读写java基本类型(byte,int,String等),但是java是面向对象的语言,必然有方便处理对象的IO。

对象序列化首先是在RMI(Remote Method Invocation)中使用的,后来在JavaBean中使用。java.io.ObjectOutputStream类提供了writeObject()方法把java对象输出到stream。java.io.ObjectInputStream类提供了readObject()方法从stream里读取对象。

9.1 Reading and Writing Objects
对象序列化(Object serialization)以byte序列保存对象的状态,根据保存的信息可以重组对象。java的序列化最初是为RMI设计的。RMI允许一台虚拟机上的对象调用另一台虚拟机上对象的方法。这需要有一种方式能把参数和返回值转变成字节流或转变自字节流,这就是对象序列化能提供的功能。

9.2 Object Streams
通过object output Stream来序列化对象,通过object input stream反序列化对象。
public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstants 
public class ObjectInputStream extends InputStream implements ObjectInput, ObjectStreamConstants


其中ObjectOutput接口是java.io.DataOutput的子接口,ObjectInput是java.io.DataInput的子接口。虽然这些都不是filter ouput stream, 但他们可以通过构造方法包装底层实际的数据流:
public ObjectOutputStream(OutputStream out) throws IOException 
public ObjectInputStream(InputStream in) throws IOException

比如:
try 
{ 
    Point p = new Point(34, 22); 
    FileOutputStream fout = new FileOutputStream("point.ser"); 
    ObjectOutputStream oout = new ObjectOutputStream(fout); 
    oout.writeObject(p); 
    oout.close(); 
} 
catch (Exception e) 
{
    System.err.println(e);
}

这样对象p的状态就被写到文件里保存起来了(也就是被序列化了)。然后我们可以反序列化,从保存状态的文件中把对象p还原:
try 
{ 
    FileInputStream fin = new FileInputStream("point.ser"); 
    ObjectInputStream oin = new ObjectInputStream(fin); 
    Object o = oin.readObject(); 
    Point p = (Point) o; 
    oin.close(); 
} 
catch (Exception e)  
{ 
    System.err.println(e); 
}


9.3 How Object Serialization Works
对象拥有属性。这些属性被保存在对象类的non-static,non-transient字段里。
如果要自己来实现把对象的属性写到流里,还要考虑父类也包含一些属性,读取的时候还要按照一定的顺序保证读写一致才能还原,这很复杂。幸运的是,Sun已经做了所有的工作。java1.1之后能读取对象的nonstatic,nontransient字段以良好的格式保存他们到流里。我们要做的就是把ObjectInputStream包装在你想把对象序列化到哪里的流(比如文件流,比如网络流)的外面,然后调用write方法就可以了。读取的时候需要显式的把对象转换成真正的类型,因为读取后返回的只是Object类型。

9.4 Performance
序列化通常在程序中是很简单的保存对象状态的方式,但是,序列化是非常低效的。如果你能为自己的程序定制一种文件格式,然后以这种格式保存程序的状态,这会比序列化快多了。
第二,序列化会延迟或阻止垃圾回收(garbage collection)。每次把对象输出到object output stream,stream会保存对这个对象的引用,直到stream被reset或close。这就意味着对象一直不能被回收,最糟糕的就是当这个stream在程序运行期间一直被打开。
解决办法就是保存对象后就关闭流,或调用流的reset方法。
public void reset() throws IOException


[b]9.5 The Serializable Interface[/b]
无条件的序列化会导致安全问题。比如,序列化能自由的访问对象的private field. 通过把object ouput stream包装在byte ouput stream, hacker能把对象转成byte array. 这样就能自由的操纵和修改对象了,然后再把篡改过的对象输出到input stream放到程序中。

安全性不是随意序列化的唯一问题,有些对象比如java.net.Socket,i/o stream只在运行的程序中有意义。这些类对象序列化没有任何意义。

基于一些考虑,java不允许任意的序列化,我们只能序列化实现了java.io.Serialaization接口的类。
public interface Serializable

这个接口没声明任何方法和属性,只是单纯的表示一个类可以被序列化。[b]实现了可以被序列化的父类的子类也潜在的允许序列化了[/b],所以我们看到很多没有显式实现这个接口的类也能被序列化。

9.5.1 Classes that implement Serializabele but are't
这个特别需要注意。一个原则上可以被序列化的类并不一定能序列化成功。好几个方面能阻止一个实现了Serializable接口的类被序列化,这时可能会抛出NotSerializableException:
public class NotSerializableException extends ObjectStreamException


Problem1: References to nonserializable objects
第一种常见的阻止一个可序列化的类被序列化的问题就是这个类包含了没有实现Serializable接口的对象引用(以及引用的引用的引用,直到引用链的末尾)。一个对象如果被序列化,那么它的所有属性,所有引用的对象也都必须能序列化。

Problem2: Missing a no-argument constructor in superclass
第二个常见的阻止可序列化类被反序列化的问题是这个类的父类不可序列化没有一个无参构造方法。java.lang.Object没有实现Serializable,所以所有的类至少有一个不可序列化的父类。当一个对象被反序列化时,这个类的继承树上最近的那个没有实现Serializable接口的父类会被调用来构造这个非序列化的父类的状态,此时如果这个父类没有无参构造方法,这个对象就不能被反序列化。

problem3: Deliberate(故意) throwing of NotSerializableException
有少部分类故意避免被序列化。有时候出于必要,比如安全或其他原因,即使一个类的父类实现了Serializable接口,子类可以选择故意在writeObject()方法里抛出NotSerializableException来避免被序列化。
private void writeObject(ObjectOutputStream out) throws IOException { 
    throw new NotSerializableException(); 
} 
private void readObject(ObjectInputStream in) throws IOException 
{ 
    throw new NotSerializableException(); 
}


9.5.2 Locating the offending object(定位导致失败的属性)
当序列化一个类的时候发生了NotSerializableException异常,这时候我们需要定位是哪个对象属性导致的,这是个很难的问题。但是NotSerializableException异常的detailMessage属性包含了这个unserialiazable类的名称,可以通过异常的getMessage()方法获取。这就减轻了很大的工作量。

9.5.3 Making nonserializable fields transient
当我们定位出了导致不能序列化的对象后,最简单的解决方案是把包含这个对象的属性设置成transient。

9.6 versioning
当一个对象被输出到stream,只有对象的状态和对象类的名字被保存下来,类定义的字节码并没有被保存。我们不能保证一个序列化的对象在相同的环境下被反序列化。序列化之后反序列化之前类的定义可能被修改。比如方法,构造方法,static和transient字段的变化。但并不是所有的变化都会阻止反序列化。比如一个类的static字段不会被序列化,所以无论是增加还是删除类的static字段都没有影响,同样序列化也会忽略掉类的所有方法。但是删除一个实例属性就会影响反序列化。这样就存在了兼容的和不兼容的修改。

9.6.1 Compatible and incompatible changes
兼容的修改就是不影响对象的序列化的修改,不兼容的修改就是那些能阻止反序列化的修改。

下面是兼容的修改的列表:
1.大多数对构造方法和实例方法的修改,不管是否为static。这里说的是大多数,例外的是跟序列化处理相关的方法,特别是跟writeObject()和readObject()方法相关的。
2.所有对static字段的修改,包括修改类型,名称,增加还是删除。序列化会忽略static字段。
3.所有对transient字段的修改。
4.增加实例字段。反序列化时,新增字段会被设置为默认值。
5.增加或删除类实现的接口(Serializable接口除外)。
6.增加和删除内部类。
7.修改字段的访问属性(public,private等)。
8.把字段从static修改为nonstatic,从transient到nontransient.这跟增加字段是一样的。

下面是非兼容的修改:
1. 修改类的名称。
2. 修改实例字段的类型。
3. 修改实例字段的名字。
4. 修改字段从nonstatic到static或nontransient到transient。这相当于删除实例字段。
5. 修改类的父类。这样可能影响对象继承的状态。
6. 以不兼容的方式修改writeObject()或readObject()方法。
7.修改类从Serializable到Externalizable或Externalizable到Serializable。

9.6.2 version ids
为了帮助识别兼容或不兼容的类修改,每个流可以有一个stream unique identifier, SUID for short,被保存到static字段里serialVersionUID.
每次发布类的不兼容修改的新版本时都应该修改这个字段。

参考:
《java I/O》 Elliotte Rusty Harold
0
0
分享到:
评论

相关推荐

    JDK 1.5的泛型實現(Generics in JDK 1.5)

    &lt;&gt;)的用法和 C++完全相同,角括號之 內的指定型別,就是同質容器的元素型別,如圖 1。 ArrayList&lt;String&gt; strList = new ArrayList&lt;String&gt;(); strList.add("zero"); strList.add("one"); strList.add("two...

    ObjectInputStream

    序列化(Serialization)是将一个Java对象转换为字节流的过程,而反序列化(Deserialization)则是将字节流恢复为原始对象的过程。这个过程在数据存储、网络传输等场景中非常常见。 `ObjectInputStream`类继承自`...

    深入浅析Java Object Serialization与 Hadoop 序列化

    Java Object Serialization 和 Hadoop 序列化都是将结构化对象转化为字节流以便在网络上传输或者写到磁盘永久存储的过程,但 Hadoop 序列化基于 Java 序列化的基础上进行了扩展和优化,提供了更高效的序列化方式。

    FST:快速Java序列化的替代品

    3. **序列化操作**:使用`FSTObjectOutput`进行序列化,`FSTObjectInput`进行反序列化,这两个类提供了类似于`ObjectOutputStream`和`ObjectInputStream`的API。 4. **优化配置**:根据具体需求,可以通过配置项...

    java序列化和反序列化

    // Object serialization try (FileOutputStream out = new FileOutputStream("serial.txt"); ObjectOutputStream oout = new ObjectOutputStream(out)) { oout.writeObject(object1); } catch (Exception e) {...

    java-6个机制.doc

    #### 二、序列化机制(Serialization Mechanism) **2.1 定义与原理** 序列化机制是将对象状态转化为二进制流的过程,便于在网络间传输或持久化到磁盘等存储介质。Java通过实现`Serializable`接口来支持对象序列化...

    基于Java的源码-存储与读取对象.zip

    Java提供`ObjectOutputStream`和`ObjectInputStream`类来处理对象的序列化和反序列化。`ObjectOutputStream`用于写入对象到输出流,而`ObjectInputStream`则用于从输入流读取对象。 ```java try (FileOutputStream ...

    什么是Java的序列化和反序列化?如何实现对象的序列化和反序列化?(java面试题附答案).txt

    2. **ObjectOutputStream与ObjectInputStream**: - `ObjectOutputStream`用于将对象写入输出流,从而实现序列化。 - `ObjectInputStream`用于从输入流中读取对象,实现反序列化。 3. **transient关键字**: - ...

    序列化Demo

    “序列化Demo”的内容可能包括了如何创建可序列化的Java类,如何使用ObjectOutputStream和ObjectInputStream进行序列化和反序列化操作,以及如何处理序列化过程中可能出现的问题,如版本控制和安全性。同时,IPCDemo...

    java 对象流 的用法

    Java提供了两种类型的对象流:ObjectInputStream和ObjectOutputStream。它们分别用于读取和写入对象。以下是一个简单的使用示例: ```java import java.io.*; public class ObjectStreamExample { public static ...

    基于java的存储与读取对象.zip

    `FileOutputStream`和`FileInputStream`用于读写文件,而`ObjectOutputStream`和`ObjectInputStream`则用于处理序列化和反序列化的对象。 4. **文件路径和URL**:在代码fans.net这个例子中,可能是指一个网络上的...

    Th09 序列化和反射Th09 序列化和反射

    序列化和反射是Java编程语言中的两个重要概念,它们在软件开发中有着广泛的应用。本文将深入探讨这两个主题,帮助你更好地理解和运用它们。 **序列化(Serialization)** 序列化是指将一个对象的状态转换为可存储或...

    \\(^_^)/ Java io 结构

    4. **对象序列化(Object Serialization)**: 序列化允许将Java对象转换为字节流,以便存储或在网络上传输。ObjectOutputStream和ObjectInputStream负责对象的序列化和反序列化。 5. **文件操作(File Operations)**: ...

    Java深复制与浅复制&Clone

    可以使用序列化(Serialization)和反序列化(Deserialization)来实现深复制,或者手动编写代码递归复制所有属性。例如: ```java public class MyClass implements Serializable { private String str; private...

    Java序列化(Serialization) 机制

    在Java中,实现序列化的主要类是`ObjectOutputStream`和`ObjectInputStream`。`ObjectOutputStream`负责将Java对象写入字节流,而`ObjectInputStream`则负责从字节流中恢复对象。以下是一个简单的示例: ```java ...

    电话薄管理系统java代码.doc

    为了实现这个电话薄管理系统,还需要扩展代码以支持文件操作,例如使用`java.io`包中的`ObjectOutputStream`和`ObjectInputStream`进行序列化和反序列化。同时,可能需要添加一个`Menu`类来实现用户友好的交互式菜单...

    java源码:Java存储与读取对象.rar

    这通常涉及到序列化(Serialization)和反序列化(Deserialization)的概念。序列化是将一个对象转换为字节流的过程,以便可以存储在磁盘上或在网络上传输。反序列化则是将字节流恢复为原来的对象实例。下面我们将...

    类的序列化

    4. **ObjectOutputStream和ObjectInputStream**:在Java中,`ObjectOutputStream`用于将对象写入输出流,实现对象的序列化;而`ObjectInputStream`则用于从输入流中读取对象,实现对象的反序列化。 5. **序列化操作...

    Java 文件 序列化 读写

    这个过程称为序列化(Serialization)和反序列化(Deserialization)。在Java中,`java.io.Serializable`接口用于标记那些可以被序列化的类。下面将详细介绍Java文件序列化读写的相关知识点。 1. **序列化的目的**...

    Java存储与读取对象.rar

    例如,使用`ObjectOutputStream`和`ObjectInputStream`,或者使用`FileWriter`, `BufferedWriter`等进行文本数据的写入,再用`FileReader`, `BufferedReader`进行读取。 3. **数据库存储**:Java可以使用JDBC(Java...

Global site tag (gtag.js) - Google Analytics