什么是对象的序列化?什么是对象的反序列化?序列化有什么用?单例模式下如何避免序列化的作用?……通过以下的文章你会得到你想要的答案!
===================================================
1.序列化的定义:
百度百科上是这样定义序列化的:将对象的状态信息转换成可存储或者可传输的形式的过程。
通俗的讲就是,把你内存中的对象转换成字节码,以便于持久性储存或进行远程的数据交换。
2.反序列化的定义
就是把序列化倒回去,从你储存或者是接收到的对象字节码中,还原出序列化时状态的对象。
3.序列化有什么用
序列化能使对象信息持久化。(当你的应用退出时,你想让用户下次打开时可以继续上一次的操作。或者是你写的小游戏需要实现“继续游戏“的功能。……)
还能进行远程的对象信息传输。(我曾做过一个小型的群聊天室,客户端发送聊天消息给服务器端,由服务器端将消息转发给其他用户。在发送消息时,我们需要知道这个消息的发送时间、发送人、消息内容。我只能把这他们三个封装到一个Message类中,让Message的对象实现信息传输。)
简单的向硬盘上存储对象信息:
private void store() throws Exception{
ObjectOutputStream oos =new ObjectOutputStream(new FileOutputStream("F:/codeTemp/object.out"));
Msg msg =new Msg();
//set content 、person、date
oos.writeObject(msg);
oos.flush();
oos.close();
}
简单的使用Socket通信中的OutputStream实现对象信息的传输:
/**
* send message
* @param os --the OutputStream get from socket
* @throws Exception
*/
private void sendMsg(OutputStream os) throws Exception{
ObjectOutputStream oos =new ObjectOutputStream(os);
Msg msg= new Msg();
//设置内容
//设置日期
//设置消息的发送人
oos.writeObject(msg);
oos.flush();
oos.close();
}
简单的从硬盘中读取对象信息
private Msg getStore() throws Exception{
ObjectInputStream ois =new ObjectInputStream(new FileInputStream("F:/codeTemp/object.out"));
Msg msg = (Msg)ois.readObject();
return msg;
}
简单的从socket通信中获取对象信息:
/**
* @param is --the inputstream from socket
* @return get message
* @throws Exception
*/
public static Msg getMsg(InputStream is) throws Exception{
ObjectInputStream ois =new ObjectInputStream(is);
Msg msg = (Msg)ois.readObject();
return msg;
}
说了这么多,和我们要探讨的序列化接口Serializable好像没什么关系啊?
注意,我们的序列化对象Msg必须实现Serializable。否则会抛出NotSerializableException。
===================================================
序列化时,你必须要知道的几点:
1.静态成员是类级别的,他不会被序列化。所以,我们不能序列化static修饰的变量。
2.Java中还为序列化定义了一个相关的变量修饰符—transient。被transient修饰的变量不会参与序列化。
3.序列化的对象中有引用型变量时,引用变量也会被序列化。依次递归序列化。
4.子类继承了可序列化的父类时,子类就可以被序列化。
5.除了使用Serializable接口,我们还可以使用Externalizable接口,但是我们必须自己去实现序列化的过程,即需要重写writeExternal(ObjectOutput out)和readExternal(ObjectInput in)这两个未实现的方法。
6.序列化中的一个非常重要的概念,序列化ID。 private static final long serialVersionUID = 1L。
7.向Object流中写对象时,若对象已经存在,则不会将对象再次进行写入,而是进行引用的写入。
8.-7的这条原则同样适用于硬盘的存储对象,java为了节省磁盘空间,不会进行重复的对象写入。
==================================================
了解java设计模式的人一定会知道,单例模式要避免序列化的来的影响。
反序列化出来的对象是原对象的深拷贝。
看如下的代码:
//单例类
public class Singleton implements Serializable {
private String name = null;
private Integer age = null;
private static final Singleton instatnce = new Singleton("John", 31);
public static Singleton getInstance() {
return instatnce;
}
private Singleton() {}
private Singleton(String name, Integer age) {
this.name = name;
this.age = age;
}
}
//测试单例类的序列化
public static void main(String args[]) throws Exception {
Singleton obj = Singleton.getInstance();
/*write obj to a file*/
ObjectOutputStream oos =new ObjectOutputStream(new FileOutputStream("F:/codeTemp/object.out"));
oos.writeObject(obj);
oos.flush();
oos.close();
/*read obj from a file*/
ObjectInputStream ois =new ObjectInputStream(new FileInputStream("F:/codeTemp/object.out"));
Singleton readObj= (Singleton) ois.readObject();
/*judge the two Object is or is not same one */
System.out.println("obj ?== readObj:"+(obj==readObj));
}
结果
obj ?== readObj:false
这就说明了反序列化出来的对象是原对象的深拷贝。
要想保持单例,我们就需要添加一个readSolve()方法,直接返回单例对象。
在io的反序列化过程中,无论是实现了Serializable接口还是实现Externalizable接口,都会调用readSolve()方法。我们若是让readSovle返回对象,那么反序列化创建的对象就不会被返回了。
//实现了readSovle()方法的单例类
public class Singleton implements Serializable {
private String name = null;
private Integer age = null;
private static final Singleton instatnce = new Singleton("John", 31);
public static Singleton getInstance() {
return instatnce;
}
private Singleton() {}
private Singleton(String name, Integer age) {
this.name = name;
this.age = age;
}
private Object readResolve() throws ObjectStreamException {
return instatnce;
}
}
重新测试一下单例类,结果:
obj ?== readObj:true
以上只是序列化的简单介绍,以后会追加更深层次的介绍。