本文是java八股文学习总结系列、内容来自于各学习文档总结。 一、序列化和反序列化 1、概念 把对象转换为字节序列的过程称为对象的序列化。 把字节序列恢复为对象的过程称为对象的反序列化。 2、用途 1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中; 2) 在网络上传送对象的字节序列。 3、API 3.1、序列化 java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。 3.2、反序列化 java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。 3.3、实现方法 只有实现了Serializable和Externalizable接口的类的对象才能被序列化。Externalizable接口继承自 Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以 采用默认的序列化方式 。 3.4、对象序列化包括如下步骤: 3.4.1、 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流; 3.4.2、通过对象输出流的writeObject()方法写对象。
3.5、对象反序列化的步骤如下: 3.5.1、 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流; 3.5.2、 通过对象输入流的readObject()方法读取对象。 3.6、示例代码: Person类:
复制代码
1 import java.io.Serializable;
3
10 public class Person implements Serializable {
12
15 private static final long serialVersionUID = -5809782578272943999L;
16 private int age;
17 private String name;
18 private String sex;
20 public int getAge() {
21 return age;
22 }
24 public String getName() {
25 return name;
26 }
28 public String getSex() {
29 return sex;
30 }
32 public void setAge(int age) {
33 this.age = age;
34 }
36 public void setName(String name) {
37 this.name = name;
38 }
40 public void setSex(String sex) {
41 this.sex = sex;
42 }
43 }
序列化和反序列化方法
import java.io.File;
2 import java.io.FileInputStream;
3 import java.io.FileNotFoundException;
4 import java.io.FileOutputStream;
5 import java.io.IOException;
6 import java.io.ObjectInputStream;
7 import java.io.ObjectOutputStream;
8 import java.text.MessageFormat;
9
10
17 public class TestObjSerializeAndDeserialize {
18
19 public static void main(String[] args) throws Exception {
20 SerializePerson();
21 Person p = DeserializePerson();
22 System.out.println(MessageFormat.format("name={0},age={1},sex={2}",
23 p.getName(), p.getAge(), p.getSex()));
24 }
25
26
33 private static void SerializePerson() throws FileNotFoundException,
34 IOException {
35 Person person = new Person();
36 person.setName("gacl");
37 person.setAge(25);
38 person.setSex("男");
39
40 ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(
41 new File("E:/Person.txt")));
42 oo.writeObject(person);
43 System.out.println("Person对象序列化成功!");
44 oo.close();
45 }
46
47
55 private static Person DeserializePerson() throws Exception, IOException {
56 ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
57 new File("E:/Person.txt")));
58 Person person = (Person) ois.readObject();
59 System.out.println("Person对象反序列化成功!");
60 return person;
61 }
62
63 }
3.7、异常 1、如果被写对象的类型是String,或数组,或Enum,或Serializable,那么就可以对该对象进行序列化,否则将抛出NotSerializableException 2、读取程序的CLASSPATH中包含有Person.class(哪怕在读取Person对象时并没有显示地使用Person类,如上例所示),否则会抛出ClassNotFoundException。 3.8、相关方法和关键字 3.8.1、transient 某个字段被声明为transient后,默认序列化机制就会忽略该字段
public class Person implements Serializable {
transient private Integer age = null;
}
3.8.2、writeObject()方法与readObject()方法
public class Person implements Serializable {
transient private Integer age = null;
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeInt(age);
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
age = in.readInt();
}
}
在writeObject()方法中会先调用ObjectOutputStream中的defaultWriteObject()方法,该方法会执行默认的序列化机制,因为age使用了transcient关键字,此时会忽略掉age字段。然后再调用writeInt()方法显示地将age字段写入到ObjectOutputStream中。readObject()的作用则是针对对象的读取,其原理与writeObject()方法相同。 其中writeObject()与readObject()都是private方法,调用他们要使用反射。 3.8.3、Externalizable接口 无论是使用transient关键字,还是使用writeObject()和readObject()方法,其实都是基于Serializable接口的序列化。JDK中提供了另一个序列化接口–Externalizable,使用该接口之后,之前基于Serializable接口的序列化机制就将失效。此时将Person类修改成如下,
public class Person implements Externalizable {
private String name = null;
transient private Integer age = null;
private Gender gender = null;
public Person() {
System.out.println("none-arg constructor");
}
public Person(String name, Integer age, Gender gender) {
System.out.println("arg constructor");
this.name = name;
this.age = age;
this.gender = gender;
}
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeInt(age);
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
age = in.readInt();
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(name);
out.writeInt(age);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
name = (String) in.readObject();
age = in.readInt();
}
}
3.8.4readResolve()方法 当我们使用Singleton模式时,应该是期望某个类的实例应该是唯一的,但如果该类是可序列化的、则从文件person.out中获取的Person对象与Person类中的单例对象并不相等。为了能在序列化过程仍能保持单例的特性,可以在Person类中添加一个readResolve()方法,在该方法中直接返回Person的单例对象,如下所示:
public class Person implements Serializable {
private static class InstanceHolder {
private static final Person instatnce = new Person("John", 31, Gender.MALE);
}
public static Person getInstance() {
return InstanceHolder.instatnce;
}
private String name = null;
private Integer age = null;
private Gender gender = null;
private Person() {
System.out.println("none-arg constructor");
}
private Person(String name, Integer age, Gender gender) {
System.out.println("arg constructor");
this.name = name;
this.age = age;
this.gender = gender;
}
private Object readResolve() throws ObjectStreamException {
return InstanceHolder.instatnce;
}
}
无论是实现Serializable接口,或是Externalizable接口,当从I/O流中读取对象时,readResolve()方法都会被调用到。实际上就是用readResolve()中返回的对象直接替换在反序列化过程中创建的对象,而被创建的对象则会被垃圾回收掉。
|