1.什么是java对象的序列化?
Serialization(序列化)是一种将对象以一连串的字节描述的过程;
与之对应,反序列化是一种将这些字节重建成一个对象的过程。
2.什么时候需要用到序列化?
Java平台允许我们在内存中创建可复用的Java对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在,即,这些对象的生命周期不会比JVM的生命周期更长。但在现实应用中,就可能要求在JVM停止运行之后能够保存(持久化)指定的对象,并在将来重新读取被保存的对象。Java对象序列化就能够帮助我们实现该功能。
使用Java对象序列化,在保存对象时,会把其状态保存为一组字节,在未来,再将这些字节组装成对象。必须注意地是,对象序列化保存的是“实例对象”的状态,即它的普通成员变量。由此可知,对象序列化不会关注类中的静态变量。简单的说,就是序列化不会保存序列化对象实例的静态变量,其反序列后的对象实例的静态成员变量的值依赖于程序,而不依赖于序列化对象时其静态变量的值!
除了在持久化对象时会用到对象序列化之外,当使用RMI(远程方法调用),或在网络中传递对象时,都会用到对象序列化。
3.序列化的具体实现:
首先,想要能够序列化的对象必须实现Serialiable接口,该接口为标志性接口,无实际内容!
然后,序列化和反序列化的本质就是对象的字节流转化,所以必然涉及到java的io相关实现。
对应io为 ObjectInputStream 和 ObjectOutputStream。
具体代码如下:
import java.util.*;
import java.io.*;
public class SerializableTest {
public static void main(String args[])
{
//将实例对象序列化
STest sTest = new STest();
System.out.println
("序列化前的信息:" + sTest.getA() + "," + sTest.getString());
SerializableTest st = new SerializableTest();
st.serializable(sTest);
STest copySTest = st.getSerializable();
System.out.println
("反序列化后的信息:" + copySTest.getA() + "," + copySTest.getString());
System.out.println("关于序列化和非序列化的克隆性比较如下:");
System.out.println(sTest == copySTest); //false,说明反序列化实际产生了新的对象实例
System.out.println(sTest.getA() == copySTest.getA()); //true
System.out.println(sTest.getString() == copySTest.getString()); //false,说明实现了深克隆
System.out.println(sTest.getString().equals(copySTest.getString())); //true
}
public void serializable(STest sTest)
{
FileOutputStream fos = null; //文件输出流,用于上层包装
ObjectOutputStream oos = null; //对象输出流,用与对象序列化的字节流传输
try {
fos = new FileOutputStream("ss.out");
oos = new ObjectOutputStream(fos);
oos.writeObject(sTest); //封装的oos把对象序列化的字节流输入进文件
oos.flush();
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
oos.close();
fos.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
public STest getSerializable()
{
FileInputStream fis = null;
ObjectInputStream ois = null;
try {
fis = new FileInputStream("ss.out");
ois = new ObjectInputStream(fis);
STest sTest = (STest) ois.readObject(); //ois把字节流反序列化为对象传入内存,向下转型为STest
return sTest;
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
ois.close();
fis.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
return null;
}
}
//封装类,实现序列化
class STest implements Serializable
{
private int a = 0;
private String string = "liu";
public void setA(int a){ this.a = a; }
public int getA(){ return a; }
public void setString(String string){ this.string = string; }
public String getString(){ return this.string; }
}
程序测试结果显示如下:
从测试结果看出,序列化和反序列化,可以实现对象的深克隆!
4.我们再来理解,静态成员变量的问题:
上面已经提到,序列化字节流,不保存类变量(成员变量)的状态信息!
通过具体例子来理解:
我们想把上述代码中实现序列化的类中的一个成员变量改为静态的。
为了简单测试,把private改为了public,然后对其int成员变量更改为类静态变量。
然后,我们实现序列化的方法不变,然后变更反序列化方法为:
增加一行语句,使其反序列之前(字节流反序列化为对象实例之前),代码逻辑显式更改类变量a!
观察程序测试结果:
很明显,我们发现,实现序列化的类静态变量,反序列化的值并不是序列化时候的值,而是依托于程序的执行逻辑的!
通俗理解:
我们不妨这样考虑,假如序列化是保存类静态变量的,那么在反序列化的时候,a应该还是0!但是,很显然不是。
所以说,序列化不关注类的静态变量,其类变量(静态变量)依托于程序!
深入理解这种机制的意义:
现在我们来考虑,为什么序列化不会关注静态变量。。。
我们不妨先来想一下,什么是静态变量,简单来说,静态变量就是所有当前所属类的所有对象实例共同拥有并维护的一个成员变量。。。任何对象对其的改变都会被其他所有对象可见,,,,所以说,静态变量并不是依附于当前类的某一个具体的对象实例,他是依附于当前整个类的(所以有时候被称为 类变量),且其存在于JVM运行时的方法区,供类和类实例访问。。。。
我们回想了静态变量后,就不难理解这个问题了。。。。假如,一个类对象实例在被序列化的时候,可以序列化静态变量,,,那就相当于把这个静态变量保存在了一个地方,相当于给静态变量产生了副本,,,这是不合理的。。。因为假如序列化之后,这个静态变量被其他地方更改了,,,那反序列化之后的这个静态变量的值很明显不应该是序列化之前的值,而应该是从方法区中读出来的值。。。所以说,序列化不应该关注类的静态变量。。。