java序列化
对象序列化机制:把内存中的java对象包装成与平台无关的二进制流,从而允许将二进制流持久保存到磁盘上,或者通过网络将这种二进制流传输到另外的节点,然后再通 过反序列化,将java对象从IO流中恢复。
序列化的必要性: java中,一切皆对象,在分布式环境中需要将Object从这一端网络到另一端,这就需要有一种可以在两端传输数据的协议,java序列化就是为了解决这个 问题。
特点:1,要序列化的对象类需要实现Serializable接口(不需要实现方法,里面是空的,没有方法),相当于声明了一个序列化的标志。
2,一个对象只能序列化一次,后面对这个对象的重复序列化并不产生作用,仍然返回第一次序列化对象的编号。即使在该对象序列化之后,修改该对象的属性,反序列 化后仍然是第一次序列化后的结果。
序列化的算法:1,将对象实例相关的类元数据输出。(实例化类的域描述)
从上面序列化的步骤中可以看出,1,序列化类的父类也必须序列化或者父类的构造函数为空也可,否则不能正常序列化。
对象序列化步骤: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方法时,对象在序列化和反序列化过程,会自动调用这两个方法,因此我们可以在相应的方法里实现自定义序列化。
法二:实现另外一个接口Externalizable,不需要再实现Serializable,实现Externalizable之后,需要实现writeExternal()和readExternal()两个方法,而不需要实现writeObject和readObject方法。就可以实现自定义序列化。
serialVersionUID的作用:
当序列化对象后,serialVersionUID可以保证,原来的Class类又被修改了,仍然可以正常序列化,不会产生不兼容问题。
如果一个类可序列化,serialVersionUID建议给一个确定的值,不要由系统自动生成,否则在增减字段(不能修改字段类型及长度)时,如果两边的类的版本不同会导致反序列化失败.