序列化和反序列化
序列化就是指把Java对象转换为字节序列的过程,
反序列化就是指把字节序列恢复为Java对象的过程。
如果直接使用io流的知识来实现,会发现一旦对象的属性过多之后,代码的繁琐程度大大增加,所以采用序列化方法。
下面这个例子就是对Person对象的三个属性进行存储,如果属性过多那么注释行代码会很庞大(此处省略Person类)
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class NotSeralizeTest {
public static void main(String[] args) {
Person person=new Person("jack",22,"襄阳");
savePerson(person);
Person person2=loadPerson();
System.out.println(person==person2);//打印结果为false,因为重新new了一个
}
private static Person loadPerson() {
BufferedReader reader = null;
try {
reader=new BufferedReader(new FileReader("D:/person2.dat"));
String content=reader.readLine();
String[] arr= content.split("_");
Person person=new Person(arr[0],Integer.parseInt(arr[1]),arr[2]);
System.out.println(person);
return person;
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}finally {
try {
reader.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
private static void savePerson(Person person) {
String content=person.getName()+"_"+person. getAge()+"_"+person.getAddress();
BufferedWriter bw=null;
try {
bw=new BufferedWriter(new FileWriter("D:/person.dat"));
bw.write(content);
bw.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
bw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
序列化过程,要让想要被保存的对象的类实现Seralizable,这个接口是个标识接口,是表面该类可以被序列化,不需要实现
该接口的方法,因为这个接口中没有一个方法。java中一共有4个这样的标识接口。简单介绍一下
java.io.Serializable:未实现此接口的类将无法使其任何状态序列化或反序列化.为保证 serialVersionUID 值跨不同
java 编译器实现的一致性,序列化类必须声明一个明确的 serialVersionUID 值.
java.lang.Cloneable:表明Object.clone()方法可以合法地对该类实例进行按字段复制.实现此接口的类应该使用公共
方法写 Object.clone(它是受保护的).如果在没有实现 Cloneable 接口的实例上调用 Object 的 clone 方法,则会导致抛出
CloneNotSupportedException 异常.
java.util.RandomAccess:用来表明其支持快速(通常是固定时间)随机访问.此接口的主要目的是允许一般的算法更改
其行为,从而在将其应用到随机或连续访问列表时能提供良好的性能.
java.rmi.Remote:Remote 接口用于标识其方法可以从非本地虚拟机上调用的接口.任何远程对象都必须直接或间接实现
此接口.只有在“远程接口”(扩展 java.rmi.Remote 的接口)中指定的这些方法才可远程使用.
现在用序列化的方式来存储Person对象。
public class SerializeTest {
public static void main(String[] args) {
Person person = new Person("jim",20,"哈尔滨");
ObjectOutputStream oos=null;
try {
oos=new ObjectOutputStream(new FileOutputStream("D:/person3.dat"));
oos.writeObject(person);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
try {
oos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Person.java
public class Person implements Serializable{
//实现Serializable接口标识类
private static final long serialVersionUID = 1L;
//如果一个类标识为可序列化的类,那么要给这个类一个默认的UID供jdk识别
private String name;
private int age;
private String address;
public Person() {
super();
}
public Person(String name, int age, String address) {
super();
this.name = name;
this.age = age;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", address=" + address + "]";
}
}
只需要new一个对象输出流即可,调用writeObject()方法。
说明一下private static final long serialVersionUID = 1L;这条语句的作用,
JAVA序列化的机制是通过判断类的serialVersionUID来验证的版本一致的。在进行反序列化时,JVM会把传来
的字节流中的serialVersionUID于本地相应实体类的serialVersionUID进行比较。如果相同说明是一致的,可以进
行反序列化,否则会出现反序列化版本一致的异常,即是InvalidCastException。
接下来将序列化与反序列化放在一起
public class SerializeTest {
public static void main(String[] args) {
Person person = new Person("jim",20,"哈尔滨");
savePerson(person);
Person person2=loadPerson();
System.out.println(person==person2);
}
private static Person loadPerson() {//反序列化
ObjectInputStream ois=null;
try {
ois=new ObjectInputStream(new FileInputStream("D:/person3.dat"));
return (Person)ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
try {
ois.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
private static void savePerson(Person person) {//序列化
ObjectOutputStream oos=null;
try {
oos=new ObjectOutputStream(new FileOutputStream("D:/person3.dat"));
oos.writeObject(person);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
try {
oos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
在序列化接口中有一个关键字叫做transient,当使用这个关键字修饰需要序列化的对象的属性时候,该属性不再
被序列化,更谈不上被反序列化。
拓展,程序员可以自定义序列化代码,只要将想要序列化的对象实现Externalizable接口即可,但此时transient
关键字不再起作用,不管有没有用它修饰属性,该属性都会被自定义的序列化方法进行序列化,除非这个序列化
方法中不对这个属性进行序列化。
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.Serializable;
public class Person implements Externalizable{
private static final long serialVersionUID = 1L;
private String name;
private int age;
private String address;
public Person() {
super();
}
public Person(String name, int age, String address) {
super();
this.name = name;
this.age = age;
this.address = address;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", address=" + address + "]";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
//重写输入流方法,反序列化过程
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.setName((String)in.readObject());
this.setAge(in.readInt());
}
//重写输出流方法,序列化过程
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(this.getName());
out.writeObject(this.getAge());
}
}