java序列化
对象序列化机制:把内存中的java对象包装成与平台无关的二进制流,从而允许将二进制流持久保存到磁盘上,或者通过网络将这种二进制流传输到另外的节点,然后再通 过反序列化,将java对象从IO流中恢复。
序列化的必要性: java中,一切皆对象,在分布式环境中需要将Object从这一端网络到另一端,这就需要有一种可以在两端传输数据的协议,java序列化就是为了解决这个 问题。
特点:1,要序列化的对象类需要实现Serializable接口(不需要实现方法,里面是空的,没有方法),相当于声明了一个序列化的标志。
2,一个对象只能序列化一次,后面对这个对象的重复序列化并不产生作用,仍然返回第一次序列化对象的编号。即使在该对象序列化之后,修改该对象的属性,反序列 化后仍然是第一次序列化后的结果。
序列化的算法:1,将对象实例相关的类元数据输出。(实例化类的域描述)
2,递归的输出类的超类的描述,直到不再有超类。(父类的域描述)
3,类元数据输出完之后,开始从最顶层的超类输出对象实例的实际数据值。(从父类开始,实例化对象的实际数据)
4,从上到下递归输出实例的数据。(递归的直到实例化类,实例化对象的实际数据)
从上面序列化的步骤中可以看出,1,序列化类的父类也必须序列化或者父类的构造函数为空也可,否则不能正常序列化。
2,如果序列化类的字段涉及到另外的类,那么这个涉及到的类也必须序列化,否则不能正常序列化。
对象序列化步骤:1,创建ObjectOutputStream对象,它需要一个节点流对象。
2,调用writeObj方法,将需要序列化的对象传入其中。
如下:
//创建一个ObjectOutputStream输出流 oos = new ObjectOutputStrea( new FileOutputStrea("object.txt")); //将序列化后的内容存到object.txt里面 Person per = new Person("dfy", 500); //需要序列化的类 //将per对象写入输出流 oos.writeObject(per);
反序列化步骤:1,创建ObjectInputStream对象,它需要一个节点流对象(这里的节点流对象与前面的序列化里面的节点流保持一致)
2,调用readObjec方法,返回的是之前序列化的对象。
如下:
ois=new OutInputStream(new FileInputStream(object.txt)); Person p=(Person)ois.readObject();//从输入流读取java对象,并强制转换为序列化对象的类型(即Person类型)
需要注意的事项:1,反序列化读取的仅仅是Java对象的数据,这里的数据只包括对象的类名和属性,(并不包括方法,静态属性,和transient属性)而不是Java对象类,因 此,在反序列化过程中,必须提供序列化对象所属的Class类文件,在反序列化后,之所以可以调用序列化后的对象,以及没有实现序列化的属性,就是这个 Class文件所起的作用。
2,序列化机制中,读出对象的顺序需要与写入的顺序一致。3,对象类的父类要么需要序列化,要么要提供无参的构造函数。
对象中的有的属性不想序列化如何办到?
使用关键字transient,如下“
private transient int age;
如何实现自定义序列化:
法一:
public class Person implements java.io.Serializable { private String name; private int age; public Person(String name , int age) { System.out.println("有参数的构器"); this.name = name; this.age = age; } public void setName(String name) { this.name = name; } public String getName() { return this.name; } public void setAge(int age) { this.age = age; } public int getAge() { return this.age; } //这里定义了对person对象的序列化机制 private void writeObject(java.io.ObjectOutputStream out) throws IOException { out.writeObject(new StringBuffer(name).reverse()); out.writeInt(age); } //这里定义了对person对象的序列化机制 private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { this.name = ((StringBuffer)in.readObject()).reverse().toString(); this.age = in.readInt(); } }
因为Serializable是空接口,里面没有方法,因此当没有添加(注意这里用了添加一词,而不是覆盖,)writeObject和readObject方法时,是实现系统默认的序列化(属性都序列化),当序列化类中添加了writeObject和readObject方法时,对象在序列化和反序列化过程,会自动调用这两个方法,因此我们可以在相应的方法里实现自定义序列化。
serialVersionUID的作用:
当序列化对象后,serialVersionUID可以保证,原来的Class类又被修改了,仍然可以正常序列化,不会产生不兼容问题。
如果一个类可序列化,serialVersionUID建议给一个确定的值,不要由系统自动生成,否则在增减字段(不能修改字段类型及长度)时,如果两边的类的版本不同会导致反序列化失败.
参考1
参考2