1.说明:
面向实现了Serialization接口的对象,可将它们转换成一系列字节,并可在以后完全恢复原来的样子。换句话说,可以在Windows机器上创建一个对象,通过序列化机制,将其通过网络发送给Linux机器,在通过反序列化可将其恢复成原来的样子。
可实现有限持久化—通过序列化机制,转换成字节流写入到磁盘,在需要的时候再取出来。(有限的原因在于它不像存到数据库中的数据,序列化机制需要自己明确指定序列化和组装对象)
2.序列化过程:
- 1.创建某些OutputStream对象
- 2.创建ObjectOutputStream对象,将某些OutputStream对象封装到ObjectOutputStream对象中
- 3.调用writeObject(object)完成对象的序列化
3.反序列化过程:
- 1.创建某些InputStream对象
- 2.创建ObjectInputStream对象,将InputStream对象封装到ObjectInputStream的对象中
- 3.调用readObject()完成反序列化
定义需要序列化的类
package com.zd.java.io.newio.serialization;
import java.io.Serializable;
/**
* 寻找类
* 序列化一个对象,通过网络将其作为文件传送给另一台机器,
* 另一台机器只更根据文件目录来重新构造此对象
* Created by ZD on 2017/10/12.
*/
public class Alien implements Serializable{
}
序列化对象
package com.zd.java.io.newio.serialization;
import java.io.*;
/**
* 创建和序列化Alien对象
* Created by ZD on 2017/10/12.
*/
public class FreezeAlien {
public static void main(String[] args) throws IOException {
ObjectOutput out = new ObjectOutputStream(new FileOutputStream("file.x"));
Alien zorcon = new Alien();
out.writeObject(zorcon);
}
}
反序列化
package com.zd.java.io.newio.serialization;
import java.io.*;
/**
* 恢复一个序列化的对象,如果想对其做更多的事,必须保证JVM能在本地类路径或者
* 因特网的其他什么地方找到相关的class文件
* Created by ZD on 2017/10/12.
*/
public class ThawAlien {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream in = new ObjectInputStream(new FileInputStream("file.x"));
Object mystery = in.readObject();
System.out.println(mystery.getClass().toString());
}
}
4.序列化的控制:
只序列化对象的一部分,其他部分不序列化,因为对象恢复后,那一部分需要重新构建。下面列出几种方式
4.1ExternalSerializable:
在Serializable的基础上扩展了,新增方法writeExternal()和readExternal(),在序列化和重新装配过程中会优先调用这俩个方法。
package com.zd.java.io.newio.serialization;
import java.io.*;
/**
*保存和恢复一个Externalizable对象必须要做的全部事情
* 恢复对象时会调用默认构造函数,在调用readExternal方法。保存对象会调用writeExternal方法
* Created by ZD on 2017/10/12.
*/
public class Blip3 implements Externalizable{
int i;
String s;
public Blip3(){
System.out.println("Blip3 constructor");
}
public Blip3(String x,int a){
System.out.println("Blip3(String z,int a)");
s = x;
i = a;
}
public String toString(){
return s + i;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
System.out.println("blip3.writeExternal");
out.writeObject(s);
out.writeInt(i);//写入对象的重要数据
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
System.out.println("Blip3.readExternal");
s = (String) in.readObject();
i = in.readInt();//恢复数据
}
public static void main(String[] args){
System.out.println("constructing objects:");
Blip3 blip3 = new Blip3("a string",47);
System.out.println(blip3.toString());
try {
ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("Blip3.out"));
System.out.println("saving object");
o.writeObject(blip3);
o.close();
ObjectInputStream in = new ObjectInputStream(new FileInputStream("Blip3.out"));
System.out.println("recovering b3:");
blip3 = (Blip3) in.readObject();
System.out.println(blip3.toString());
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
说明:使用ExternalSerializable,重新组装对象需要调用默认构建函数,再调用readExternal()方法,序列化对象时会调用writeExternal()方法,在这俩个方法中均需要对重要数据进行操作。如果不进行操作,对象成员数据为成默认数据,操作可能会出现问题。
4.2transient(临时)关键字:
对象中如果存在敏感部分,比如登录中的密码,我们不希望它在序列化过程中被保存下来,因为可能会被别人盗取。一种办法是采用ExternalSerializable,只有在writeObject()中明确序列化,否则不保存。另一种办法采用transient关键字。
做法:实现Serializable接口,将不想序列化的字段设置成transient
package com.zd.java.io.newio.serialization;
import java.io.*;
import java.util.Date;
/**
* 使用transient进行序列化控制
* 除密码外其他会话信息需要保存下来
* Created by ZD on 2017/10/12.
*/
public class Logon implements Serializable{
private Date date = new Date();
private String username;
private transient String password;//transient不会被保存到磁盘
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.toString()+
"\n password:"+pwd;
}
public static void main(String[] args){
Logon a = new Logon("hulk","mylittlepony");
System.out.println("logon a = "+a);
try {
ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("Logon.out"));
o.writeObject(a);
o.close();
//delay
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);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
执行结果:
logon a = logon info:
username:hulk
date:Sat Oct 14 11:04:34 CST 2017
password:mylittlepony
recovering object at Sat Oct 14 11:04:40 CST 2017
logon a = logon info:
username:hulk
date:Sat Oct 14 11:04:34 CST 2017
password:(n/a)
4.3ExternalSerializable替代方法:
可以实现Serializable接口,并在类中添加俩个方法:writeObject()、readObject(),方法需要有正确的签名:
private void writeObject(ObjectOutputStream stream)throws IOException;
private void readObject(ObjectIutputStream stream)throws IOException;
package com.zd.java.io.newio.serialization;
import java.io.*;
/**
* 使用Serializable实现Externalizable的功能
* 不管是否有transient关键字,优先使用writeObject、readObject
* Created by ZD on 2017/10/12.
*/
public class SerialCtl implements Serializable {
String a;
transient String b;
public SerialCtl(String aa,String bb){
a = "not transient:"+aa;
b = "transient"+bb;
}
public String toString(){
return a + "\n" +b;
}
private void writeObject(ObjectOutputStream stream) throws IOException {
stream.defaultWriteObject();//必须调用
stream.writeObject(b);
}
private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
stream.defaultReadObject();
b = (String) stream.readObject();
}
public static void main(String[] args){
SerialCtl sc = new SerialCtl("test1","test2");
System.out.println("before:\n"+sc);
ByteArrayOutputStream buf = new ByteArrayOutputStream();
try {
ObjectOutputStream o = new ObjectOutputStream(buf);
o.writeObject(sc);//必须检查sc,判断它是否有自己的writeObject()方法
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buf.toByteArray()));
SerialCtl sc2 = (SerialCtl) in.readObject();
System.out.println("a filter:\n"+sc2);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
5.总结:
序列化方案:
1.实现Serializable接口,自动序列化writeObject(Object object)方法中定义的object,重新组装时是从InputStream中读入的字节进行重新组装对象
2.实现ExternalSerializable接口,在writeExternal()方法中指定需要进行序列化的字段,在 readExternal()方法中指定需要重新组装的字段,组装对象时,调用的是默认构造函数。
3.transient关键字,实现Serializable接口,对不需要序列化的字段声明为transient
4.使用Serializable替代ExternalSerializable接口,在类中添加readObject()、writeObject()方法。