`

java序列化

阅读更多
本文介绍Java序列化技术。
1.Java的"对 象序列化"
Java 的"对象序列化"能让你将一个实现了Serializable接口的对象转换成一组byte,这样日后要用这个对象时候,你就能把这些byte数据恢复出 来,并据此重新构建那个对象。这一点甚至在跨网络的环境下也是如此,这就意味着序列化机制能自动补偿操作系统方面的差异。也就是说,你可以 在 Windows机器上创键一个对象,序列化之后,再通过网络传到Unix机器上,然后在那里进行重建。你不用担心在不同的平台上数据是怎样表示 的, byte顺序怎样,或者别的什么细节。
对象序列化最聪明的一点是,它不仅能保存对象的副本,而且还会跟踪对象里面的reference,把它所引用的对象也保存起来,然后再 继续跟踪那些对象的 reference,以此类推。这种情形常被称为"单个对象所联结的'对象网'"。这个机制所涵盖的范围不仅包括对象的成员数据,而 且还包含数组里面的 reference。如果你要自己实现对象序列化的话,那么编写跟踪这些链接的程序将会是一件非常痛苦的任务。但是,Java的对象 序列化就能精确无误地 做到这一点,毫无疑问,它的遍历算法是做过优化的。
2.Object serialization的定义
Object serialization 允许你将实现了 Serializable接口的对象转换为字节序列,这些字节序列可以被完全存储以备以后重新生成原来的对象。
serialization不但可以在本机做,而且可以经由网络操作 (RMI)。这个好处是很大的----因为它自动屏蔽了操作系统的差异,字节顺序(用Unix下的c开发过网络编程的人应该知道这个概念)等。比如,在 Window平台生成一个对象并序列化之,然后通过网络传到一台Unix机器上, 然后可以在这台Unix机器上正确地重构这个对象。
Object serialization主要用来支持2种主要的特性:
Java的 RMI(remote method invocation).RMI允许象在本机上一样操作远程机器上的对象。当发送消息给远程对象时,就需要用到 serializaiton机制来发送参数和接收返回直。
Java的JavaBeans。Bean的状态信息通常是在设计时配置的。Bean的状态信息必须被存起来,以便当程序运行时能恢复这 些状态信息。这也需要serializaiton机制。
3.一般序列化实例
程序名称:SerializationDemo.java
程序主题:实现对象的序列化和反序列化
程序说明:该程序由实例化一个MyClass类的对象开始,该对象有三个 实例变量,类型分别为String、int、double,是希望存储和恢复的信息。
import java.io.Serializable;
class MyClass implements Serializable {
    String s;
    int i;
    double d;

    public MyClass(String s, int i, double d) {
        this.s = s;
       this.i = i;
       this.d = d;
    }

    public String toString() {
       return "s=" + s + ";i=" + i + ";d=" + d;
    }
}
要想序列化对象,你必须先创建一个OutputStream,然后把它嵌 进ObjectOutputStream。这时,你就能用writeObject ( )方法把对象写入OutputStream了。读的时候,你得把 InputStream嵌到ObjectInputStream里面,然后再调用 readObject( )方法。
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class SerializationDemo {
        public static void main(String args[]) {
       FileInputStream in = null;
       FileOutputStream out = null;
       ObjectInputStream oin = null;
       ObjectOutputStream oout = null;
       MyClass object1 = new MyClass("Hello", -7, 2.7e10);
       System.out.println("object1:" + object1);
       // Object serialization
       try {
           out = new FileOutputStream("serial.txt");
           oout = new ObjectOutputStream(out);
           oout.writeObject(object1);
           oout.close();
       } catch (Exception e) {
           System.out.println("Exception during serialization:" + e);
           System.exit(0);
       }

       // Object deserialization
        try {
           MyClass object2;
           in = new FileInputStream("serial");
           oin = new ObjectInputStream(in);
           object2 = (MyClass) oin.readObject();
           oin.close();
           System.out.println("object2:" + object2);
       } catch (Exception e) {
           System.out.println("Exception during deserialization:" + e);
           System.exit(0);
       } finally {
           // … 此处省略
       }
    }
}
结果:
object1:s=Hello;i=-7;d=2.7E10
object2:s=Hello;i=-7;d=2.7E10
4.修改默认的序列化机制
在序列化的过程中,有些数据字段我们不想将其序列化,对于此类字段我们只 需要在定义时给它加上transient关键字即可,对于transient字段序列化机制会跳过不会将其写入文件,当然也不可被恢复。但有时我们想将某 一字段序列化,但它在SDK中的定义却是不可序列化的类型,这样的话我们也必须把他标注为transient,可是不能写入又怎么恢复呢?好在序列化机制 为包含这种特殊问题的类提供了如下的方法定义:
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;
private void writeObject(ObjectOutputStream out) throws IOException;
(注:这些方法定义时必须是私有的,因为不需要你显示调用,序列化机制会 自动调用的)使用以上方法我们可以手动对那些你又想序列化又不可以被序列化的数据字段进行写出和读入操作。
下面是一个典型的例子,java.awt.geom包中的 Point2D.Double类就是不可序列化的,因为该类没有实现Serializable接口,在我的例子中将把它当作LabeledPoint类中 的一个数据字段,并演示如何将其序列化。
import java.awt.geom.Point2D;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
class LabeledPoint implements Serializable {
    private String label;
    transient private Point2D.Double point;

    public LabeledPoint(String label, double x, double y) {
       super();
       this.label = label;
       this.point = new Point2D.Double(x, y);
    }

    private void writeObject(ObjectOutputStream oos) throws IOException {
       oos.defaultWriteObject();// 先 序列化对象
       oos.writeDouble(point.getX());
       oos.writeDouble(point.getY());
    }

    private void readObject(ObjectInputStream ois) throws IOException,
           ClassNotFoundException {
       ois.defaultReadObject();// 先 反序列化对象
       double x = ois.readDouble() + 1.0;
       double y = ois.readDouble() + 1.0;
       point = new Point2D.Double(x, y);
    }

    public String toString() {
       return getClass().getName() + "[ Label = " + label
              + ", point.getX() = " + point.getX() + ",
point.getY() = " + point.getY() + "]";
    }
}

public class Test1 {
    public static void main(String[] args) {
       LabeledPoint label = new LabeledPoint("Book", 5.0, 5.0);
       try {
           System.out.println("before:\n" + label);
           ObjectOutputStream oos = new ObjectOutputStream(
                  new FileOutputStream("label.txt"));
           oos.writeObject(label);
           oos.close();

           ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("label.txt"));
           LabeledPoint label1 = (LabeledPoint) ois.readObject();
           ois.close();
           System.out.println("after add 1.0:\n" + label1);
       } catch (Exception e) {
           e.printStackTrace();
       }
    }
}
结果:
before:
sample.LabeledPoint[ Label = Book, point.getX() = 5.0, point.getY() = 5.0]
after add 1.0:
sample.LabeledPoint[ Label = Book, point.getX() = 6.0, point.getY() = 6.0]
5.继承类序列化实例
当一个父类实现Serializable接口后,他的子类都将自动的实现 序列化。
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class SuperC implements Serializable {// 父 类实现了序列化
    int supervalue;

    public SuperC(int supervalue) {
       this.supervalue = supervalue;
    }

    public String toString() {
       return "supervalue: " + supervalue;
    }
}

class SubC extends SuperC {// 子 类
    int subvalue;

    public SubC(int supervalue, int subvalue) {
       super(supervalue);
       this.subvalue = subvalue;
    }

    public String toString() {
       return super.toString() + " sub: " + subvalue;
    }
}

public class Test2 {
    public static void main(String[] args) {
       SubC subc = new SubC(100, 200);
       FileInputStream in = null;
       FileOutputStream out = null;
       ObjectInputStream oin = null;
       ObjectOutputStream oout = null;
       try {
           out = new FileOutputStream("Test1.txt");
           oout = new ObjectOutputStream(out);
           oout.writeObject(subc); // 子 类序列化
           oout.close();
           oout = null;

           in = new FileInputStream("Test1.txt");
           oin = new ObjectInputStream(in);
           SubC subc2 = (SubC) oin.readObject(); // 子类反序列化
           System.out.println(subc2);
       } catch (Exception ex) {
           ex.printStackTrace();
       } finally {
           // … 此处省略
       }
    }
}
结果:
supervalue: 100 sub: 200
可见子类成功的序列化/反序列化了。怎管让子类实现序列化看起来是一件很简 单的事情,但有的时候,往往我们不能够让父类实现Serializable接口,原因是有时候父类是抽象的(这并没有关系),并且父类不能够强制每个子类 都拥有序列化的能力。换句话说父类设计的目的仅仅是为了被继承。
要为一个没有实现Serializable接口的父类,编写一个能够序列化的子类要做两件事情:
其一、父类要有一个无参的constructor;
其二、子类要负责序列化(反序列化)父类的域。
  我们将SuperC的Serializable接口去掉,而给 SubC加上Serializable接口。运行后产生错误:
java.lang.Error: Unresolved compilation problem:
Serializable cannot be resolved or is not a valid superinterface
at Serial.SubC.<init>(SubC.java:15)
at Serial.Test1.main(Test1.java:19)
Exception in thread "main"
我们改写这个例子:
abstract class SuperC{
    int supervalue;

    public SuperC(int supervalue) {
       this.supervalue = supervalue;
    }

    public SuperC() {}//增加一个无参的constructor
   
    public String toString() {
       return "supervalue: " + supervalue;
    }
}

class SubC extends SuperC implements Serializable{// 子 类
    int subvalue;

    public SubC(int supervalue, int subvalue) {
       super(supervalue);
       this.subvalue = subvalue;
    }

    public String toString() {
       return super.toString() + " sub: " + subvalue;
    }
   
    private void writeObject(java.io.ObjectOutputStream out) throws IOException {
       out.defaultWriteObject();// 先 序列化对象
       out.writeInt(supervalue);// 再序列化父类的域
    }

    private void readObject(java.io.ObjectInputStream in) throws IOException,
           ClassNotFoundException {
       in.defaultReadObject();// 先 反序列化对象
       supervalue = in.readInt();//再反序列化父类的域
    }
}
运行结果证明了这种方法是正确的。在此处我们又用到 了 writeObject/ readObject方法,以代替默认的行为。我们在序列化时,首先调用了 ObjectOutputStream的 defaultWriteObject,它使用默认的序列化行为,然后序列化父类的域;反序列化的时候也一样。
6.实现Externalizable接 口
Externalizable 接口继承自Serializable接口,如果一个类实现了Externalizable接口,那么将完全由这个类控制自身的序列化行为。 Externalizable接口声明了两个方法:
public void readExternal(ObjectInput in) throws IOException , ClassNotFoundException;
public void writeExternal(ObjectOutput out) throws IOException;
前者负责序列化操作,后者负责反序列化操作。
在对实现了Externalizable接口的类的对象进行反序列化时, 会先调用类的不带参数的构造方法(回忆前两个例子,异曲同工),这是有别于默认反序列方式的。如果把类的不带参数的构造方法删除,或者把该构造方法的访问 权限设置为private、默认或protected级别,会抛 出 java.io.InvalidException: no valid constructor异常。
class ExternalDemo implements Externalizable { // ExternalDemo 类必须实现Externalizable接口
    private String aString = "TEST";
    private int num = 0;

    public ExternalDemo() {}

    public void writeExternal(ObjectOutput out) throws IOException {
       out.writeObject(aString);
       out.write(88); // 在 序列化的数据最后加个88
    }

    public void readExternal(ObjectInput in) throws IOException,
           ClassNotFoundException {
       aString = (String) in.readObject();
       num = in.read(); // 把 数字88加进来
    }

    public String toString() { // 测试
       return ("String:"+aString + " int:"+num);
    }
}

public class Test3 {
    public static void main(String[] args) {
       ExternalDemo eDemo = new ExternalDemo();
       try {
           ObjectOutputStream oos = new ObjectOutputStream(
                  new FileOutputStream("test3.txt"));
           oos.writeObject(eDemo); // writeExternal() 自动执行
           oos.close();

           ObjectInputStream ois = new ObjectInputStream(
                  new FileInputStream("test3.txt"));
           ExternalDemo demo = (ExternalDemo) ois.readObject(); // readExternal()自动执行
           System.out.print(demo);
           ois.close();
       } catch (Exception e) {
           e.printStackTrace();
       }
    }
}
结果:
String:TEST int:88
7.可序列化类的不同版本的序列化兼容性
凡是实现Serializable接口的类都有一个表示序列化版本标识符 的静态变量:
private static final long serialVersionUID ;
以上serialVersionUID的取值是Java运行时环境根据类 的内部细节自动生成的。如果对类的源代码作了修改,再重新编译,新生成的类文件的serialVersionUID的取值有可能也会发生变化。
类的serialVersionUID的默认值完全依赖于Java编译器 的实现,对于同一个类,用不同的Java编译器编译,有可能会导致不同的 serialVersionUID,也有可能相同。为了提高 serialVersionUID的独立性和确定性,强烈建议在一个可序列化类中显示的定义serialVersionUID,为它赋予明确的值。显式地 定义serialVersionUID有两种用途:
在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;
在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本 具有不同的serialVersionUID。
8.总结Java序列化技术
如果一个对象的成员变量是一个对象,那么这个对象的数据成员也会被保存;
当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列 化;
当一个父类实现序列化,子类自动实 现序列化,不需要显式实现Serializable接口;
如果一个可序列化的对象包含对某个不可序列化的对象的引用,那么整个序列化操作将会失败,并且会抛出一个 NotSerializableException。我们可以将这个引用标记为transient,那么对象仍然可以序列化;
并非所有的对象都可以序列化,,至于为什么不可以,有很多原因。

另见:http://wenku.baidu.com/view/cbdea8fdc8d376eeaeaa31ee.html
分享到:
评论

相关推荐

    Java序列化

    Java序列化是Java平台中的一种标准机制,允许将对象的状态转换为字节流,以便存储在磁盘上、通过网络进行传输或者在某些时候恢复原来的对象状态。这一过程包括两个主要步骤:对象的序列化(将对象转换为字节流)和反...

    Java序列化_Java序列化结构_

    Java序列化是Java平台中的一种持久化机制,它允许对象的状态被转换成字节流,以便存储、网络传输或在不同时间点恢复。这个过程被称为序列化,而反向操作称为反序列化。序列化在许多场景下都非常有用,比如在分布式...

    java序列化和反序列化的方法

    java 序列化和反序列化的方法 Java 序列化和反序列化是 Java 语言中的一种机制,用于将对象转换为字节流,以便在网络上传输或存储。序列化是将对象转换为字节流的过程,而反序列化是将字节流转换回对象的过程。 在...

    java序列化(Serializable)的作用和反序列化.doc

    ### Java序列化(Serializable)的作用与反序列化详解 #### 一、序列化的概念 序列化是指将程序中的对象转换为一系列字节流的过程,主要用于保存对象的状态或在网络之间传输对象。序列化的主要目的是为了能够持久化...

    Protocol Buffer序列化对比Java序列化.

    【Protocol Buffer序列化对比Java序列化】 Protocol Buffer(简称PB)是Google开发的一种高效的数据序列化协议,而Java序列化是Java平台内置的一种序列化机制。两者的主要目标都是将对象转化为字节数组,便于在网络...

    java 序列化时排除指定属性

    Java序列化是Java平台提供的一种持久化机制,它允许我们将一个Java对象转换为字节流,以便存储到磁盘上,或者通过网络进行传输。这使得我们可以保存和恢复对象的状态。实现序列化的类需要实现`Serializable`接口,...

    java序列化全解

    Java序列化是Java平台中的一种核心机制,它允许对象的状态被转换成字节流,以便存储到磁盘、数据库,或者在网络中进行传输。这对于实现持久化、远程方法调用(RMI)以及Enterprise JavaBeans(EJB)等高级功能至关...

    java序列化(Serializable)的作用和反序列化

    ### Java序列化(Serializable)的作用与反序列化详解 #### 一、序列化是什么? 序列化是指将程序中的对象转换为字节流的过程,从而方便存储或传输这些对象。通常,序列化用于将对象的状态(即其实例变量的值,而非...

    java 序列化代码示例

    Java序列化是Java平台中的一种标准机制,它允许将对象的状态转换为字节流,以便存储、传输或恢复。在Java中,一个类如果要实现序列化,需要实现`Serializable`接口,这是一个标记接口,不包含任何方法。下面我们将...

    Java序列化Jar包

    Java序列化是Java平台中的一项重要技术,它允许对象的状态被转换为字节流,以便存储或通过网络进行传输。这种技术在分布式系统、持久化存储以及数据交换等场景中非常常见。本资源包含了三个流行的Java序列化框架:...

    Java序列化的机制和原理

    Java序列化是Java平台提供的一种将对象转换为字节流,以便存储、在网络上传输或者在后续时间重新创建相同对象的机制。这是Java编程中一个非常重要的概念,尤其是在分布式环境和持久化存储中。让我们深入探讨一下Java...

    java序列化原理与算法

    ### Java序列化原理与算法详解 #### 序言 在现代软件开发中,尤其是在网络通信和数据持久化领域,对象的序列化与反序列化扮演着至关重要的角色。Java作为一种广泛应用的编程语言,提供了强大的内置支持来实现序列化...

    07-Java序列化面试题(10题)-新增.pdf

    Java序列化面试题(10题) 在 Java 中,序列化是一种用于处理对象流的机制,它可以将对象的内容进行流化,使其可以被读写和传输。下面是 10 个与 Java 序列化相关的面试题目: 1. 什么是 Java 序列化,如何实现 ...

    java序列化实现演示

    Java序列化是Java平台中的一种标准机制,允许对象的状态被保存到磁盘或者在网络中进行传输,以便在后续的时间或地点恢复这些对象。这个过程包括两个主要操作:序列化(将对象转换为字节流)和反序列化(将字节流恢复...

    Java序列化多次追加到txt以及从txt反序列化

    Java序列化是Java平台提供的一种持久化对象的机制,它允许我们将对象的状态转换为字节流,以便存储或在网络上传输。在这个特定的场景中,我们关注的是如何使用Java序列化来多次追加对象到一个TXT文件,而不是覆盖...

    java序列化和反序列化

    ### Java序列化与反序列化详解 #### 一、Java序列化概述 Java序列化(Serialization)是一项重要的功能,它可以将对象的状态转化为一系列字节,从而实现对象的持久化存储或在网络上传输。序列化机制使得Java对象...

    Java对象序列化标准最新版

    ### Java对象序列化标准知识点详解 #### 一、系统架构概览 **1.1 概览** Java 对象序列化是一种将Java对象的...以上内容涵盖了Java序列化标准的关键知识点,深入了解这些概念有助于更好地理解和应用Java序列化技术。

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

    **FST:快速Java序列化的替代方案** 在Java开发中,序列化是一个常见的需求,它允许将对象的状态转换为字节流,以便于存储或网络传输。标准的Java序列化虽然方便,但在处理大量数据时,性能往往成为瓶颈。这时,FST...

    E043-服务漏洞利用及加固-利用Java序列化漏洞进行渗透测试.pdf

    Java序列化漏洞是一种常见的安全问题,它出现在Java应用程序中,当对象被转化为字节流以便在网络间或存储中传输时。这种序列化过程如果处理不当,可能会导致远程代码执行(RCE)、信息泄露或者权限提升等严重后果。...

    java序列化对象传给php

    android(包括java)序列化一个对象传给php去做处理,或是接到php的序列化的对象在java中做处理的工具jar包以及使用方法. 使用方法: byte[] b = null; b = PHPSerializer.serialize(一个对象);//将一个对象序列化后返回...

Global site tag (gtag.js) - Google Analytics