序列化(Serialization)

一、序列化是什么?
序列化支持对象编码为字节流和并从中获得对象。序列化支持对象图像从流中重构。用于轻量级的持久化和通过Socket和RMI的通信。默认的对象编码方法保护私有和临时数据,支持类的演变。一个类可以实现自己的外部编码方法,然后唯一地负责外部格式。
Object Serialization supports the encoding of objects and the objects reachable from them, into a stream of bytes. Serialization also supports the complementary reconstruction of the object graph from a stream. Serialization is used for lightweight persistence and for communication via sockets or Java Remote Method Invocation (Java RMI). The default encoding of objects protects private and transient data, and supports the evolution of the classes. A class may implement its own external encoding and is then solely responsible for the external format.

序列化现在包含一个API,允许对象的序列化数据的指定独立于类的字段,并允许序列化数据从流中读或写,使用现有的协议(协商、商议)保证与默认的读写机制的兼容。
Serialization now includes an API that allows the serialized data of an object to be specified independently of the fields of the class and allows those serialized data fields to be written to and read from the stream using the existing protocol to ensure compatiblity with the default writing and reading mechanisms.


[b]序列化的应用场景:
1) 对数据传输的处理;
2) 对数据加密的处理;
3) 对数据压缩的处理
4) 对版本更新的处理
5) 对继承关系的处理[/b]

[b]序列化的实现手段:
1) 默认序列化技术:继承Serializable接口,可重写writeObject/readObject方法。
2) 外部序列化技术:继承Externalizable接口,实现writeExternal/readExternal方法。
3) 替换技术:继承Serializable接口, 实现writeReplace/readResolve进行预编码与解析,读与写不需要平衡,也可以使用代理技术。
4) 使用序列化字段API: 使用ObjectStreamField指定序列化字段,使用对象流中内部类PutField和GetField的putFields/getFieldsf方法,批量将对象的字段写入流或从流中读出。[/b]

二、序列化技术的应用
1)[b]序列化技术中对象的传输[/b]
在客户端将对象序列化写入流中,在服务器将从流中反序列化读取对象。客户端代码示例,序列化。

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

public class Client {
public static void main(String args[]) {
try {
// Create a socket
Socket soc = new Socket(InetAddress.getLocalHost(), 8020);

// Serialize today's date to a outputstream associated to the socket
OutputStream o = soc.getOutputStream();
ObjectOutput s = new ObjectOutputStream(o);

s.writeObject("Today's date");
s.writeObject(new Date());
s.flush();
s.close();
} catch (Exception e) {
System.out.println(e.getMessage());
System.out.println("Error during serialization");
System.exit(1);
}
}
}


服务器代码示例,反序列化。

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

public class Server {
/**
* Create the serversocket and use its stream to receive serialized objects
*/
public static void main(String args[]) {
ServerSocket ser = null;
Socket soc = null;
String str = null;
Date d = null;

try {
ser = new ServerSocket(8020);
/*
* This will wait for a connection to be made to this socket.
*/
soc = ser.accept();
InputStream o = soc.getInputStream();
ObjectInput s = new ObjectInputStream(o);
str = (String) s.readObject();
d = (Date) s.readObject();
s.close();

// print out what we just received
System.out.println(str);
System.out.println(d);
} catch (Exception e) {
System.out.println(e.getMessage());
System.out.println("Error during serialization");
System.exit(1);
}
}
}


2)[b]序列化技术对数据的加密[/b]

package serializable;

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 Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;

public String getName() { return name;}
public int getAge() { return age; }
public void setAge(int age) { this.age = age;}
public void setName(String name) { this.name = name;}

private void writeObject(ObjectOutputStream oos) throws IOException {
// Encrypt
this.age <<= 3;
oos.defaultWriteObject();
}
private void readObject(ObjectInputStream ios) throws IOException, ClassNotFoundException {
// Decrypt
ios.defaultReadObject();
this.age >>= 3;
}
}

class SerializableMain {
public static void main(String[] args) throws Exception {
Person object = new Person();
object.setName("Lily");
object.setAge(18);

// Serializing
FileOutputStream fos = new FileOutputStream("Person");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(object);
oos.close();
fos.close();

// Deserializing
FileInputStream fis = new FileInputStream("Person");
ObjectInputStream ios = new ObjectInputStream(fis);
object = (Person) ios.readObject();
fis.close();
ios.close();
System.out.println(object.getAge());
System.out.println(object.getName());
System.out.println(object.getClass());
}
}



3)[b]序列化技术对数据的压缩[/b]
这个例子展示了如何使用writeObject和readObject对自定义数据格式进行编码。当持久化的数据是笨重的,应当采用更轻捷、紧凑的格式来存储它。该例子中的涉及到一个三角形矩阵,一个三角形矩阵是一个二维对称的矩阵,所以序列化时,仅需要存储一半的数据,而不是全部。


import java.io.*;
/*
* This will print out two arrays: one from before serialization and the other
* from after deserialization.
*/
public class CustomDataExample implements Serializable {
private static final long serialVersionUID = 1L;
transient int dimension;
transient int thearray[][];

/**
* Create the triangular array of dimension dim and initialize it
*/
CustomDataExample(int dim) {
dimension = dim;
thearray = new int[dim][dim];
arrayInit();
}

/**
* Create an CustomDataExample object, serialize it, deserialize it and see
* that they are the same. So, basically test that this custom data
* example's serialization works.
*/
public static void main(String args[]) {
CustomDataExample corg = new CustomDataExample(4);
CustomDataExample cnew = null;

// Serialize the original class object
try {
FileOutputStream fo = new FileOutputStream("cde.tmp");
ObjectOutputStream so = new ObjectOutputStream(fo);
so.writeObject(corg);
so.flush();
so.close();
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}

// Deserialize in to new class object
try {
FileInputStream fi = new FileInputStream("cde.tmp");
ObjectInputStream si = new ObjectInputStream(fi);
cnew = (CustomDataExample) si.readObject();
si.close();
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}

// Print out to check the correctness
System.out.println();
System.out.println("Printing the original array...");
System.out.println(corg);
System.out.println();
System.out.println("Printing the new array...");
System.out.println(cnew);
System.out.println();
System.out.println("The original and new arrays should be the same!");
System.out.println();
}

/**
* Write out the dimension and 1/2 of the 2-dimensional array to the
* ObjectOutputStream s. readObject depends on this data format.
*
* @serialData Write serializable fields, if any exist. Write out the
* integer Dimension of the symetrical, two-dimensional array.
* Write out the integers composing 1/2 of the 2-dimensional
* array.
*
*/
private void writeObject(ObjectOutputStream s) throws IOException {
// Call even if there is no default serializable fields.
s.defaultWriteObject();

// save the dimension
s.writeInt(dimension);

// write out only 1/2 of the 2-dimensional array
for (int i = 0; i < dimension; i++) {
for (int j = 0; j <= i; j++) {
s.writeInt(thearray[i][j]);
}
}
}

/**
* Read in the dimension and 1/2 of the 2-dimensional array from the
* ObjectInputStream s. Was written to by writeObject. Also, copy the 1/2
* array to the other half to completely fill the symmetric array.
*
* @serialData Read serializable fields, if any exist. Read optional data
* consisting of an integer indicating both dimensions of the
* 2-dimensional array. Read in 1/2 of the 2-dimensional array.
*/
private void readObject(ObjectInputStream s) throws IOException,
ClassNotFoundException {
/*
* Call even if there is no default serializable fields. Enables default
* serializable fields to be added in future versions and skipped by
* this version which has no default serializable fields.
*/
s.defaultReadObject();

// restore the dimension
dimension = s.readInt();

// allocate space for the array
thearray = new int[dimension][dimension];

// first restore 1/2 the 2-dimensional array
for (int i = 0; i < dimension; i++) {
for (int j = 0; j <= i; j++) {
thearray[i][j] = s.readInt();
}
}

// copy over to the other side
for (int i = 0; i < dimension; i++) {
for (int j = dimension - 1; j > i; j--) {
thearray[i][j] = thearray[j][i];
}
}
}

/**
* Initialize the array to some numbers starting from 0 - make it
* symmetrical
*/
void arrayInit() {
int x = 0;
for (int i = 0; i < dimension; i++) {
for (int j = 0; j <= i; j++) {
thearray[i][j] = x;
thearray[j][i] = x;
x++;
}
}
}

/**
* Print the 2-dimensional array. Useful for testing.
*/
public String toString() {

StringBuffer sb = new StringBuffer();
for (int i = 0; i < dimension; i++) {
for (int j = 0; j < dimension; j++) {
sb.append(Integer.toString(thearray[i][j]) + " ");
}
sb.append("\n");
}
return (sb.toString());
}
}

4)[b]序列化技术对版本更新的处理[/b]
序列化机制仅保存对象的类型信息,属性的类型信息和属性值,与方法无关。序列化允许重构:将新字段添加到类中,将字段从 static 改为非 static,将字段从 transient 改为非 transient。如果采用默认的内部序列化机制,对象中字段去掉修饰符private, static, transient或增加字段,不需要去改写read/write Object方法。

5)[b]序列化技术对继承关系的处理[/b]
父类是否实现了序列化,一个是无,一个是有;有的话,序列化的方式是采用默认的内部实现,还是外部的实现?于是,根据父类的序列化的不同情况,子类的序列化需要采取不同的策略。
a)父类实现默认内部序列化接口(子类使用默认化的序列实现就可以了,即实现序列化接口)。
b)父类没有实现内部序列化接口(子类必须实现read/writeObject,显式序列化父类的状态,自身的状态可以采用默认的实现)。
c)父类实现外部序列化接口(子类必须重写read/writeExternal,显式序列化父类与子类的状态,可复用父类的实现)。
d)父类没有实现外部序列化接口(子类必须实现read/writeExternal,显式序列化父类与子类的状态)。

三、序列化技术的实现手段
1)默认序列化技术:继承Serializable接口,可重写writeObject/readObject方法。
2)外部序列化技术:继承Externalizable接口,实现writeExternal/readExternal方法。

public class ReadingMaterial implements Externalizable {

/*
* In order for us to be able to serialize this data, these must be either
* serializable/externalizable objects or primitive data types.
*/
private String author;
private String subject;
private int yearwritten;


/*
* Must have a public no-arg constructor when implementing Externalizable
*/
public ReadingMaterial() {
}


public ReadingMaterial(String auth, String sub, int year) {
author = auth;
subject = sub;
yearwritten = year;
}

public String getAuthor() {
return author;
}

public String getSubject() {
return subject;
}


public int getYearwritten() {
return yearwritten;
}


public void writeExternal(ObjectOutput out) throws IOException {

out.writeObject(author);
out.writeObject(subject);
out.writeInt(yearwritten);
}

public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {

author = (String) in.readObject();
subject = (String) in.readObject();
yearwritten = in.readInt();
}
}



import java.io.*;

public class Book extends ReadingMaterial implements Externalizable {
private int numpages;
private String name;
private boolean ishardcover;

/**
* mandatory public no-arg constructor
*/
public Book() { super(); }

public Book(int pages, String n, boolean hardcover, String author,
String subject, int yearwritten) {

super(author, subject, yearwritten);
numpages = pages;
name = n;
ishardcover = hardcover;
}

public void writeExternal(ObjectOutput out) throws IOException {

// first we call the writeExternal of the superclass as to write
// all the superclass data fields
super.writeExternal(out);

// now we take care of this class's fields
out.writeInt(numpages);
out.writeObject(name);
out.writeBoolean(ishardcover);
}

public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
// first call the superclass external method
super.readExternal(in);

// now take care of this subclass's fields
numpages = in.readInt();
name = (String)in.readObject();
ishardcover= in.readBoolean();
}

/**
* Prints out the fields. used for testing!
*/
public String toString() {
return("Name: " + name + "\n" + "Author: " + super.getAuthor() + "\n" + "Pages: " + numpages + "\n" + "Subject: " + super.getSubject() + "\n" + "Year: " + super.getYearwritten() + "\n" );
}
}




import java.io.*;

public class Savesuper {
/**
* Create an Book (subclass of reading material) object, serialize it,
* deserialize it and see that they are the same. So, basically test that
* this Externalizable example's works
*/
public static void main(String args[]) {

// create a Book object
Book bookorg = new Book(100, "How to Serialize", true, "R.R", "Serialization", 97);
Book booknew = null;

//serialize the book
try {
FileOutputStream fo = new FileOutputStream("tmp");
ObjectOutputStream so = new ObjectOutputStream(fo);
so.writeObject(bookorg);
so.flush();
} catch (Exception e) {
System.out.println(e);
System.exit(1);
}

// de-serialize the Book
try {
FileInputStream fi = new FileInputStream("tmp");
ObjectInputStream si = new ObjectInputStream(fi);
booknew = (Book) si.readObject();
}
catch (Exception e) {
System.out.println(e);
System.exit(1);
}

/*
* Print out the original and new book information
* It should be the same if we did everything correctly!
*/
System.out.println();
System.out.println("Printing original book...");
System.out.println(bookorg);
System.out.println("Printing new book... ");
System.out.println(booknew);
System.out.println("Both original and new should be the same!");
System.out.println();
}
}


3)替换技术:继承Serializable接口, 实现writeReplace/readResolve进行预编码与解析,读与写不需要平衡,也可以应用代理技术。

public class Substitute {
/**
* Basically, serialize and deserialize two symbols with the same
* name and show that they are actually the same symbol.
*/
public static void main(String args[]) {

// create a few symbols to be serialized
Symbol s1 = Symbol.symbolLookup("blue");
Symbol s2 = Symbol.symbolLookup("pink");
Symbol s3 = Symbol.symbolLookup("blue");

// use these to deserialize the symbols
Symbol obj1 = null, obj2 = null, obj3 = null;


// serialize the symbols
try {
FileOutputStream fo = new FileOutputStream("symbol.tmp");
ObjectOutputStream so = new ObjectOutputStream(fo);
so.writeObject(s1);
so.writeObject(s2);
so.writeObject(s3);
so.flush();
} catch (Exception e) {
System.out.println(e);
System.exit(1);
}

// deserialize the symbols
try {
FileInputStream fi = new FileInputStream("symbol.tmp");
ObjectInputStream si = new ObjectInputStream(fi);
obj1 = (Symbol) si.readObject();
obj2 = (Symbol) si.readObject();
obj3 = (Symbol) si.readObject();
} catch (Exception e) {
System.out.println(e);
System.exit(1);
}
// show the uniqueness
if (obj1 == obj3) {
System.out.println("Symbol1 and Symbol3 are the same!");
}
System.out.println(obj1.symbolname);
System.out.println(obj2.symbolname);
System.out.println(obj3.symbolname);
}
}


/**
* The class implementing the readResolve method.
*/
class Symbol implements Serializable {
private static final long serialVersionUID = 1L;

/**
* @serial
*/
String symbolname;

/*
* Hashtable is **static** because we need to use the same one for
* all symbol objects.
*/
static Hashtable<String, Symbol> ht = new Hashtable<String, Symbol>();
/**
* This method serves as the constructor. It looks in the hashtable and
* if that symbol exists, will return that symbol... otherwise, will
* create a symbol with that name and will add it to the hashtable. This
* will assure that the symbols are always unique.
*/
static Symbol symbolLookup(String symname) {
if (!ht.containsKey(symname)) {
ht.put(symname, new Symbol(symname));
}
return (ht.get(symname));
}

/**
* Private constructor because we want "outsiders" to use
* symbolLookup instead to force uniqueness.
*/
private Symbol (String name) {
symbolname = name;
}
/**
* Deals with the issue of uniqueness when we are dealing with more
* than one VM by adding the read symbol to the hash table, if it
* isn't already there.
*/
public Object readResolve() throws ObjectStreamException {
if (!ht.containsKey(symbolname)) {
ht.put(symbolname, this);
}
return (ht.get(symbolname));
}
}



class PersonProxy implements java.io.Serializable {
private static final long serialVersionUID = 1L;

public PersonProxy(Person orig) {
data = orig.getFirstName() + "," + orig.getLastName() + ","
+ orig.getAge();
if (orig.getSpouse() != null) {
Person spouse = orig.getSpouse();
data = data + "," + spouse.getFirstName() + ","
+ spouse.getLastName() + "," + spouse.getAge();
}
}

public String data;

private Object readResolve() throws java.io.ObjectStreamException {
String[] pieces = data.split(",");
Person result = new Person(pieces[0], pieces[1], Integer
.parseInt(pieces[2]));
if (pieces.length > 3) {
result.setSpouse(new Person(pieces[3], pieces[4], Integer
.parseInt(pieces[5])));
result.getSpouse().setSpouse(result);
}
return result;
}
}

class Person implements java.io.Serializable {
public Person(String fn, String ln, int a) {
this.firstName = fn;
this.lastName = ln;
this.age = a;
}

private Object writeReplace() throws java.io.ObjectStreamException {
return new PersonProxy(this);
}

public String getFirstName() { return firstName; }
public String getLastName() { return lastName; }
public int getAge() { return age; }
public Person getSpouse() { return spouse; }
public void setFirstName(String value) { firstName = value; }
public void setLastName(String value) { lastName = value; }
public void setAge(int value) { age = value; }
public void setSpouse(Person value) { spouse = value; }

public String toString() {
return "[Person: firstName=" + firstName + " lastName=" + lastName
+ " age=" + age + " spouse=" + spouse.getFirstName() + "]";
}

private String firstName;
private String lastName;
private int age;
private Person spouse;
}


4)使用序列化字段API: 使用ObjectStreamField指定序列化字段,使用putFields/getFieldsf方法,批量将对象的字段写入流或从流中读出。

public class EvolvedClass {
public static void main(String args[]) {

ARectangle orgClass = new ARectangle(100, 100, 102, 102);
ARectangle newClass = null;

boolean serialize = false;
boolean deserialize = false;

if (args.length == 1) {
if (args[0].equals("-d")) {
deserialize = true;
} else if (args[0].equals("-s")) {
serialize = true;
} else {
usage();
System.exit(0);
}
} else {
usage();
System.exit(0);
}

/*
* Serialize the original class if that's the option chosen
*/
if (serialize) {
try {
FileOutputStream fo = new FileOutputStream("evolve.tmp");
ObjectOutputStream so = new ObjectOutputStream(fo);
so.writeObject(orgClass);
so.flush();
} catch (Exception e) {
System.out.println(e);
System.exit(1);
}
}

if (deserialize) {
try {
FileInputStream fi = new FileInputStream("evolve.tmp");
ObjectInputStream si = new ObjectInputStream(fi);
newClass = (ARectangle) si.readObject();
} catch (Exception e) {
System.out.println(e);
System.exit(1);
}
System.out.println("Now printing deserialized object: ");
System.out.println();
System.out.println(newClass);
}
}

static void usage() {
System.out.println("Usage:");
System.out.println(" -s (in order to serialize)");
System.out.println(" -d (in order to deserialize)");
}
}


class ARectangle implements java.io.Serializable {
Point point1;
Point point2;
static final long serialVersionUID = 9030593813711490592L;

private static final ObjectStreamField[] serialPersistentFields = {
new ObjectStreamField("x1", Integer.TYPE),
new ObjectStreamField("y1", Integer.TYPE),
new ObjectStreamField("x2", Integer.TYPE),
new ObjectStreamField("y2", Integer.TYPE)
};

ARectangle(int x1, int y1, int x2, int y2) {
point1 = new Point(x1, y1);
point2 = new Point(x2, y2);
}

/**
* writeObject - Writes out the serializable fields
* (the 4 integers, x1, y1, x2, y2) using the
* Serializable Field API. (the methods putFields and
* writeFields of the ObjectOutputStream Class and the method put
* of the ObjectOutputStream.PutField inner class)
*
* @serialData Only the serializable fields of the class are written.
* No optional data written.
*/
private void writeObject(ObjectOutputStream s)
throws IOException {

// set the values of the Serializable fields
ObjectOutputStream.PutField fields = s.putFields();
fields.put("x1", point1.x);
fields.put("y1", point1.y);
fields.put("x2", point2.x);
fields.put("y2", point2.y);

// save them
s.writeFields();
}

/**
* readsObject - Reads in the serializable fields
* (the 4 integers, x1, y1, x2, y2) using the
* Serializable Field API. (the methods getFields and
* readFields of the ObjectInputStream Class and the method get
* of the ObjectOutputStream.GetField inner class)
*
* @serialData No optional data is read.
*/
private void readObject(ObjectInputStream s)
throws IOException {

// prepare to read the alternate persistent fields
ObjectInputStream.GetField fields = null;
try {
fields = s.readFields();
} catch (Exception ClassNotFoundException) {
throw new IOException();
}

// read the alternate persistent fields
int x1 = (int)fields.get("x1", 0);
int y1 = (int)fields.get("y1", 0);
int x2 = (int)fields.get("x2", 0);
int y2 = (int)fields.get("y2", 0);

// save them back as Points.
point1 = new Point(x1, y1);
point2 = new Point(x2, y2);
}

public String toString() {
return("point1.x: " + point1.x + "\npoint1.y: " + point1.y + "\npoint2.x: " + point2.x + "\npoint2.y: " + point2.y);
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值