浅析java序列化

一 Java序列化和反序列化介绍

     java序列化是在JDK 1.1中引入的,是Java内核的重要特性之一。

Java序列化将一个对象转换为流,也即一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、

有关对象的类型的信息和存储在对象中数据的类型。将序列化对象写入文件之后,可以从文件中读取出来,

并且对它进行反序列化。反序列化则是将对象流转换为实际程序中使用的Java对象的过程。

也就是说,对象的类型信息、对象的数据,还有对象中的数据类型可以用来在内存中新建对象。

整个过程都是Java虚拟机(JVM)独立的,也就是说,在一个平台上序列化的对象可以在另一个完全不同的

平台上反序列化该对象。序列化可以用于轻量级的持久化、通过 Sockets 进行传输、或者用于 Java RMI。

可序列化的对象需要实现java.io.Serializable接口或者java.io.Externalizable接口。

以实现Serializable接口为例,Serializable仅是一个标记接口,并不包含任何需要实现的具体方法。

实现该接口只是为了声明该Java类的对象是可以被序列化的。

    实际的序列化和反序列化工作是通过ObjectOuputStream和ObjectInputStream来完成的。

ObjectOutputStream的writeObject方法可以把一个Java对象写入到流中,

ObjectInputStream的readObject方法可以从流中读取一个Java对象。

在写入和读取的时候,虽然用的参数或返回值是单个对象,但实际上操纵的是一个对象图,

包括该对象所引用的其它对象,以及这些对象所引用的另外的对象。

Java 会自动帮你遍历对象图并逐个序列化。除了对象之外,Java中的基本类型和数组

也是可以通过 ObjectOutputStream 和 ObjectInputStream 来序列化的。

注意,一个类的对象要想序列化成功,必须满足两个条件:

1)该类必须直接或间接实现 java.io.Serializable 对象。

2)该类的所有属性必须是可序列化的。如果有一个属性不是可序列化的,则该属性必须注明是短暂的。

如果你想知道一个Java标准类是否是可序列化的,请查看该类的文档。检验一个类的实例是否能序列化十分简单, 

只需要查看该类有没有直接或间接的实现java.io.Serializable接口。

二 序列化对象

ObjectOutputStream类用来序列化一个对象,如下的 SerializeDemo例子实例化了一个Employee对象,

并将该对象序列化到一个文件中。该程序执行后,就创建了一个名为employee.ser文件。

该程序没有任何输出,但是你可以通过代码研读来理解程序的作用。

注意:当序列化一个对象到文件时, 按照 Java 的标准约定是给文件一个.ser 扩展名。

Employee实体对象:

package com.lanhuigu.io.serializable;

import java.io.Serializable;

/**
 * 实现序列化接口--Serializable
 */
public class Employee implements Serializable {
    private static final long serialVersionUID = 8182014220967586096L;
    /** 姓名 */
    public String name;
    /** 地址 */
    public String address;
    /** 年龄 */
    public transient int age;
    /** 编号 */
    public int number;
}

 关于serialVersionUID:

     如果可序列化类未显式声明serialVersionUID,则序列化运行时将基于该类的各个方面计算该类的默认serialVersionUID值,

如“Java(TM)对象序列化规范”中所述。不过,强烈建议所有可序列化类都显式声明serialVersionUID值,

原因是计算默认的serialVersionUID对类的详细信息具有较高的敏感性,根据编译器实现的不同可能千差万别,

这样在反序列化过程中可能会导致意外的InvalidClassException。因此,为保证serialVersionUID值跨不同 

java编译器实现的一致性,序列化类必须声明一个明确的serialVersionUID值。

还强烈建议使用 private 修饰符显示声明serialVersionUID(如果可能),

原因是这种声明仅应用于直接声明类--serialVersionUID 字段作为继承成员没有用处。

数组类不能声明一个明确的 serialVersionUID,因此它们总是具有默认的计算值,

但是数组类没有匹配 serialVersionUID 值的要求。

SerializeDemo序列化例子:

package com.lanhuigu.io.serializable;


import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

/**
 * 序列化
 */
public class SerializeDemo {
    public static void main(String [] args) {
        Employee e = new Employee();
        e.name = "yi";
        e.address = "beijing";
        e.age = 27;
        e.number = 101;
        try (FileOutputStream fileOut = new FileOutputStream("C:\\mycode\\employee.ser");
             ObjectOutputStream out = new ObjectOutputStream(fileOut)) {
            out.writeObject(e);
            out.close();
            fileOut.close();
            System.out.printf("序列化数据保存在: C:\\mycode\\employee.ser");
        } catch(IOException i) {
            i.printStackTrace();
        }
    }
}

该程序运行后,序列化对象保存位置C:\\mycode\\employee.ser。

三 反序列化对象

下面例子将C:\\mycode\\employee.ser对象反序列化为Employee对象。

package com.lanhuigu.io.serializable;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

/**
 * 反序列化
 */
public class DeserializeDemo {
    public static void main(String [] args) {
        Employee e = null;
        try (
                FileInputStream fileIn = new FileInputStream("C:\\mycode\\employee.ser");
                ObjectInputStream in = new ObjectInputStream(fileIn)
          ) {
            e = (Employee) in.readObject();
            in.close();
            fileIn.close();
        } catch(IOException i) {
            i.printStackTrace();
            return;
        } catch(ClassNotFoundException c) {
            System.out.println("Employee class not found");
            c.printStackTrace();
            return;
        }
        System.out.println("===反序列化===");
        System.out.println("name: " + e.name);
        System.out.println("address: " + e.address);
        System.out.println("age: " + e.age);
        System.out.println("number: " + e.number);
    }
}

四 序列化总结

1)序列化就是对象转换为字节流和字节流转换为对象的过程,含序列化和反序列化两个过程。

2)序列化对象需要直接或间接实现Serializable接口。

3)序列化和反序列化工作是通过ObjectOuputStream和ObjectInputStream来生成流,并将流反序列化为对象。

4)声明为static和transient类型的成员数据不能被序列化。因为static代表类的状态, transient代表对象的临时数据。 


猜你喜欢

转载自blog.csdn.net/yhl_jxy/article/details/80860938