1、什么是序列化和反序列化
Serialization(序列化)是一种将对象以一连串的字节描述的过程;
反序列化deserialization是一种将这些字节重建成一个对象的过程。
2、什么情况下需要序列化
a)当你想把的内存中的对象保存到一个文件中或者数据库中时候;
b)当你想用套接字在网络上传送对象的时候;
c)当你想通过RMI传输对象的时候;
3、如何实现序列化
将需要序列化的类实现Serializable接口就可以了,Serializable接口中没有任何方法,可以理解为一个标记,即表明这个类可以序列化。
4、实例
1.定义一个可串行化对象
一个类,如果要使其对象可以被串行化,必须实现Serializable接口。我们定义一个类Student如下:
public class Student implements Serializable {
int id;// 学号
String name;// 姓名
int age;// 年龄
String department; // 系别
//getter,setter方法....
public Student(int id, String name, int age, String department) {
this.id = id;
this.name = name;
this.age = age;
this.department = department;
}
}
2.构造对象的输入/输出流
要串行化一个对象,必须与一定的对象输出/输入流联系起来,通过对象输出流将对象状态保存下来,再通过对象输入流将对象状态恢复。
java.io包中,提供了ObjectInputStream和ObjectOutputStream将数据流功能扩展至可读写对象 。在ObjectInputStream 中用readObject()方法可以直接读取一个对象,
ObjectOutputStream中用writeObject()方法可以直接将对象保存到输出流中。
@Test
public void test() throws IOException, ClassNotFoundException {
Student stu = new Student(151226, "Tom", 18, "CSD");
//把内存对象转变为字节流写到data.ser里
FileOutputStream fo = new FileOutputStream("data.ser");
//对象输出流
ObjectOutputStream so = new ObjectOutputStream(fo);
try {
so.writeObject(stu);
so.close();
} catch (IOException e) {
e.printStackTrace();
}
Student stu2 = null;
//从JVM中把字节流还原对象
FileInputStream fi = new FileInputStream("data.ser");
ObjectInputStream si = new ObjectInputStream(fi);
try {
stu2 = (Student) si.readObject();
si.close();
} catch (IOException e) {
e.printStackTrace();
}
assert stu2 != null;
System.out.println("Student Info:");
System.out.println("ID:" + stu2.id);
System.out.println("Name:" + stu2.name);
System.out.println("Age:" + stu2.age);
System.out.println("Dep:" + stu2.department);
}
运行结果 : Student Info: ID:151226 Name:Tom Age:18 Dep:CSD
4、序列化
如果我们想要序列化一个对象,首先要创建某些OutputStream(如FileOutputStream、ByteArrayOutputStream等),然后将这些OutputStream封装在一个ObjectOutputStream中。
这时候,只需要调用writeObject()方法就可以将对象序列化,并将其发送给OutputStream(记住:对象的序列化是基于字节的,不能使用Reader和Writer等基于字符的层次结构)。
而反序列的过程(即将一个序列还原成为一个对象),需要将一个InputStream(如FileInputstream、ByteArrayInputStream等)封装在ObjectInputStream内,然后调用readObject()即可。
public class MyTest implements Serializable {
private static final long serialVersionUID = 1L;
private String name = "changWen";
private int age = 22;
@Test
public void testSerializable() throws IOException, ClassNotFoundException {
//以下代码实现序列化。
// 输出流保存的文件名为 my.out ;ObjectOutputStream能把Object输出成Byte流
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("my.out"));
MyTest myTest1 = new MyTest();
os.writeObject(myTest1);
os.flush(); //缓冲流
os.close(); //关闭流
// 反序列化
MyTest myTest2 = null;
ObjectInputStream oi = new ObjectInputStream(new FileInputStream("my.out"));
//由Object对象向下转型为MyTest对象
myTest2 = (MyTest) oi.readObject();
oi.close();
System.out.println("name=" + myTest2.name);
System.out.println("age=" + myTest2.age);
}
}
会在此项目的工作空间生成一个 my.out文件。序列化后的内容稍后补齐,先看反序列化后输出如下:name=changWen age=22
5、序列化ID
序列化 ID 在 Eclipse 下提供了两种生成策略,一个是固定的 1L,一个是随机生成一个不重复的 long 类型数据(实际上是使用 JDK 工具生成),在这里有一个建议,如果没有特殊需求,就是用默认的1L 就可以,这样可以确保代码一致时反序列化成功。这也可能是造成序列化和反序列化失败的原因,因为不同的序列化id之间不能进行序列化和反序列化。
6.序列化前和序列化后的对象的关系
是"=="还是equal? or 是浅复制还是深复制?
答案:深复制,反序列化还原后的对象地址与原来的的地址不同
7.静态变量能否序列化
若把上面的代码中的 age变量前加上 static ,输出任然是:name=changWen age=22
但是看下面的例子:
public class MyTest implements Serializable {
private static final long serialVersionUID = 1L;
private String name = "changWen";
private static int age = 22;
@Test
public void testSerializable() throws IOException, ClassNotFoundException {
//以下代码实现序列化。
// 输出流保存的文件名为 my.out ;ObjectOutputStream能把Object输出成Byte流
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("my.out"));
MyTest myTest1 = new MyTest();
os.writeObject(myTest1);
os.flush(); //缓冲流
os.close(); //关闭流
// 反序列化
new MyTest().name="changWen_1";//!!!!!!!!!!!!!!!!重点看这两行的更改部分 没有static修饰的不变
age=1; //有static修饰的变
ObjectInputStream oi = new ObjectInputStream(new FileInputStream("my.out"));
MyTest myTest2 = null;
//由Object对象向下转型为MyTest对象
myTest2 = (MyTest) oi.readObject();
oi.close();
System.out.println("name=" + myTest2.name);
System.out.println("age=" + myTest2.age);
}
}
输出结果为:name=changWen age=1
为何把最上面代码的age变量添上static 后还是反序列化出了24呢?而新的从新对变量赋值的代码,不是static的得到了序列化本身的值,而static的则得到的是从新附的值。原因: 序列化会忽略静态变量,即序列化不保存静态变量的状态。静态成员属于类级别的,所以不能序列化。即 序列化的是对象的状态不是类的状态。这里的不能序列化的意思,是序列化信息中不包含这个静态成员域。最上面添加了static后之所以还是输出24是因为该值是JVM加载该类时分配的值。注:transient后的变量也不能序列化,但是情况稍复杂,稍后开篇说。
8、总结:
a)当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口;
b)当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化;
c) static,transient后的变量不能被序列化;