序列化
序列化,是为了保存对象的状态;反序列化,则可以把保存的对象状态再读出来。
序列化/反序列化,是Java提供一种专门用于的保存/恢复对象状态的机制
一般在以下几种情况下,可能会用到序列化:
a)当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;
b)当你想用套接字在网络上传送对象的时候;
c)当你想通过RMI传输对象的时候。
若要支持序列化,除了“自定义实现Serializable接口的类”之外;java的“基本类型”和“java自带的实现了Serializable接口的类”,都支持序列化
(01) 序列化对static和transient变量,是不会自动进行状态保存的。
transient的作用就是,用transient声明的变量,不会被自动序列化。
(02) 对于Socket, Thread类,不支持序列化。若实现序列化的接口中,有Thread成员;在对该类进行序列化操作时,编译会出错!若希望程序能编译通过,我们对Thread变量添加static或transient修饰即可
主要是基于资源分配方面的原因。如果Socket,Thread类可以被序列化,但是被反序列化之后也无法对他们进行重新的资源分配;再者,也是没有必要这样实现。
/** * 序列化的演示测试程序 * */ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class SerialTest3 { private static final String TMP_FILE = ".serialtest3.txt"; public static void main(String[] args) { // 将“对象”通过序列化保存 testWrite(); // 将序列化的“对象”读出来 testRead(); } /** * 将Box对象通过序列化,保存到文件中 */ private static void testWrite() { try { // 获取文件TMP_FILE对应的对象输出流。 // ObjectOutputStream中,只能写入“基本数据”或“支持序列化的对象” ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream(TMP_FILE)); // 创建Box对象,Box实现了Serializable序列化接口 Box box = new Box("desk", 80, 48); // 将box对象写入到对象输出流out中,即相当于将对象保存到文件TMP_FILE中 out.writeObject(box); // 打印“Box对象” System.out.println("testWrite box: " + box); out.close(); } catch (Exception ex) { ex.printStackTrace(); } } /** * 从文件中读取出“序列化的Box对象” */ private static void testRead() { try { // 获取文件TMP_FILE对应的对象输入流。 ObjectInputStream in = new ObjectInputStream( new FileInputStream(TMP_FILE)); // 从对象输入流中,读取先前保存的box对象。 Box box = (Box) in.readObject(); // 打印“Box对象” System.out.println("testRead box: " + box); in.close(); } catch (Exception e) { e.printStackTrace(); } } } /** * Box类“支持序列化”。因为Box实现了Serializable接口。 * * 实际上,一个类只需要实现Serializable即可实现序列化,而不需要实现任何函数。 */ class Box implements Serializable { private static int width; private transient int height; private String name; public Box(String name, int width, int height) { this.name = name; this.width = width; this.height = height; } @Override public String toString() { return "["+name+": ("+width+", "+height+") ]"; } }
运行结果
testWrite box: [desk: (80, 48) ] testRead box: [desk: (80, 0) ]
结果分析:
我们前面说过,“序列化不对static和transient变量进行状态保存”。因此,testWrite()中保存Box对象时,不会保存width和height的值。这点是毋庸置疑的!但是,为什么testRead()中读取出来的Box对象的width=80,而height=0呢?
先说,为什么height=0。因为Box对象中height是int类型,而int类型的默认值是0。
再说,为什么width=80。这是因为height是static类型,而static类型就意味着所有的Box对象都共用一个height值;而在testWrite()中,我们已经将height初始化为80了。因此,我们通过序列化读取出来的Box对象的height值,也被就是80。
/** * 序列化的演示测试程序 * * @author skywang */ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class SerialTest4 { private static final String TMP_FILE = ".serialtest4.txt"; public static void main(String[] args) { // 将“对象”通过序列化保存 testWrite(); // 将序列化的“对象”读出来 testRead(); } /** * 将Box对象通过序列化,保存到文件中 */ private static void testWrite() { try { // 获取文件TMP_FILE对应的对象输出流。 // ObjectOutputStream中,只能写入“基本数据”或“支持序列化的对象” ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream(TMP_FILE)); // 创建Box对象,Box实现了Serializable序列化接口 Box box = new Box("desk", 80, 48); // 将box对象写入到对象输出流out中,即相当于将对象保存到文件TMP_FILE中 out.writeObject(box); // 打印“Box对象” System.out.println("testWrite box: " + box); // 修改box的值 box = new Box("room", 100, 50); out.close(); } catch (Exception ex) { ex.printStackTrace(); } } /** * 从文件中读取出“序列化的Box对象” */ private static void testRead() { try { // 获取文件TMP_FILE对应的对象输入流。 ObjectInputStream in = new ObjectInputStream( new FileInputStream(TMP_FILE)); // 从对象输入流中,读取先前保存的box对象。 Box box = (Box) in.readObject(); // 打印“Box对象” System.out.println("testRead box: " + box); in.close(); } catch (Exception e) { e.printStackTrace(); } } } /** * Box类“支持序列化”。因为Box实现了Serializable接口。 * * 实际上,一个类只需要实现Serializable即可实现序列化,而不需要实现任何函数。 */ class Box implements Serializable { private static int width; private transient int height; private String name; public Box(String name, int width, int height) { this.name = name; this.width = width; this.height = height; } @Override public String toString() { return "["+name+": ("+width+", "+height+") ]"; } }
运行结果:
testWrite box: [desk: (80, 48) ] testRead box: [desk: (100, 0) ]
对前面的SerialTest4.java进行简单修改,以达到:序列化存储static和transient变量的目的
/** * 序列化的演示测试程序 * */ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.io.IOException; import java.lang.ClassNotFoundException; public class SerialTest5 { private static final String TMP_FILE = ".serialtest5.txt"; public static void main(String[] args) { // 将“对象”通过序列化保存 testWrite(); // 将序列化的“对象”读出来 testRead(); } /** * 将Box对象通过序列化,保存到文件中 */ private static void testWrite() { try { // 获取文件TMP_FILE对应的对象输出流。 // ObjectOutputStream中,只能写入“基本数据”或“支持序列化的对象” ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream(TMP_FILE)); // 创建Box对象,Box实现了Serializable序列化接口 Box box = new Box("desk", 80, 48); // 将box对象写入到对象输出流out中,即相当于将对象保存到文件TMP_FILE中 out.writeObject(box); // 打印“Box对象” System.out.println("testWrite box: " + box); // 修改box的值 box = new Box("room", 100, 50); out.close(); } catch (Exception ex) { ex.printStackTrace(); } } /** * 从文件中读取出“序列化的Box对象” */ private static void testRead() { try { // 获取文件TMP_FILE对应的对象输入流。 ObjectInputStream in = new ObjectInputStream( new FileInputStream(TMP_FILE)); // 从对象输入流中,读取先前保存的box对象。 Box box = (Box) in.readObject(); // 打印“Box对象” System.out.println("testRead box: " + box); in.close(); } catch (Exception e) { e.printStackTrace(); } } } /** * Box类“支持序列化”。因为Box实现了Serializable接口。 * * 实际上,一个类只需要实现Serializable即可实现序列化,而不需要实现任何函数。 */ class Box implements Serializable { private static int width; private transient int height; private String name; public Box(String name, int width, int height) { this.name = name; this.width = width; this.height = height; } private void writeObject(ObjectOutputStream out) throws IOException{ out.defaultWriteObject();//使定制的writeObject()方法可以利用自动序列化中内置的逻辑。 out.writeInt(height); out.writeInt(width); //System.out.println("Box--writeObject width="+width+", height="+height); } private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException{ in.defaultReadObject();//defaultReadObject()补充自动序列化 height = in.readInt(); width = in.readInt(); //System.out.println("Box---readObject width="+width+", height="+height); } @Override public String toString() { return "["+name+": ("+width+", "+height+") ]"; } }
运行结果:
testWrite box: [desk: (80, 48) ] testRead box: [desk: (80, 48) ]
程序说明:
“序列化不会自动保存static和transient变量”,因此我们若要保存它们,则需要通过writeObject()和readObject()去手动读写。
(01) 通过writeObject()方法,写入要保存的变量。writeObject的原始定义是在ObjectOutputStream.java中,我们按照如下示例覆盖即可:
private void writeObject(ObjectOutputStream out) throws IOException{
out.defaultWriteObject();// 使定制的writeObject()方法可以利用自动序列化中内置的逻辑。
out.writeInt(ival); // 若要保存“int类型的值”,则使用writeInt()
out.writeObject(obj); // 若要保存“Object对象”,则使用writeObject()
}
(02) 通过readObject()方法,读取之前保存的变量。readObject的原始定义是在ObjectInputStream.java中,我们按照如下示例覆盖即可:
private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException{
in.defaultReadObject(); // 使定制的readObject()方法可以利用自动序列化中内置的逻辑。
int ival = in.readInt(); // 若要读取“int类型的值”,则使用readInt()
Object obj = in.readObject(); // 若要读取“Object对象”,则使用readObject()
}
如果一个类要完全负责自己的序列化,则实现Externalizable接口,而不是Serializable接口。
Externalizable接口定义包括两个方法writeExternal()与readExternal()。需要注意的是:声明类实现Externalizable接口会有重大的安全风险。writeExternal()与readExternal()方法声明为public,恶意类可以用这些方法读取和写入对象数据。如果对象包含敏感信息,则要格外小心。
/** * 序列化的演示测试程序 * * @author skywang */ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectOutput; import java.io.ObjectInput; import java.io.Serializable; import java.io.Externalizable; import java.io.IOException; import java.lang.ClassNotFoundException; public class ExternalizableTest1 { private static final String TMP_FILE = ".externalizabletest1.txt"; public static void main(String[] args) { // 将“对象”通过序列化保存 testWrite(); // 将序列化的“对象”读出来 testRead(); } /** * 将Box对象通过序列化,保存到文件中 */ private static void testWrite() { try { // 获取文件TMP_FILE对应的对象输出流。 // ObjectOutputStream中,只能写入“基本数据”或“支持序列化的对象” ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream(TMP_FILE)); // 创建Box对象 Box box = new Box("desk", 80, 48); // 将box对象写入到对象输出流out中,即相当于将对象保存到文件TMP_FILE中 out.writeObject(box); // 打印“Box对象” System.out.println("testWrite box: " + box); out.close(); } catch (Exception ex) { ex.printStackTrace(); } } /** * 从文件中读取出“序列化的Box对象” */ private static void testRead() { try { // 获取文件TMP_FILE对应的对象输入流。 ObjectInputStream in = new ObjectInputStream( new FileInputStream(TMP_FILE)); // 从对象输入流中,读取先前保存的box对象。 Box box = (Box) in.readObject(); // 打印“Box对象” System.out.println("testRead box: " + box); in.close(); } catch (Exception e) { e.printStackTrace(); } } } /** * Box类实现Externalizable接口 */ class Box implements Externalizable { private int width; private int height; private String name; public Box() { } public Box(String name, int width, int height) { this.name = name; this.width = width; this.height = height; } @Override public void writeExternal(ObjectOutput out) throws IOException { } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { } @Override public String toString() { return "["+name+": ("+width+", "+height+") ]"; } }
运行结果:
testWrite box: [desk: (80, 48) ] testRead box: [null: (0, 0) ]
说明:
(01) 实现Externalizable接口的类,不会像实现Serializable接口那样,会自动将数据保存。
(02) 实现Externalizable接口的类,必须实现writeExternal()和readExternal()接口!
否则,程序无法正常编译!
(03) 实现Externalizable接口的类,必须定义不带参数的构造函数!
否则,程序无法正常编译!
(04) writeExternal() 和 readExternal() 的方法都是public的,不是非常安全!
接着,我们修改上面的ExternalizableTest1.java测试程序;实现Box类中的writeExternal()和readExternal()接口!
修改后的源码如下( ExternalizableTest2.java):
/** * 序列化的演示测试程序 * * @author skywang */ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectOutput; import java.io.ObjectInput; import java.io.Serializable; import java.io.Externalizable; import java.io.IOException; import java.lang.ClassNotFoundException; public class ExternalizableTest2 { private static final String TMP_FILE = ".externalizabletest2.txt"; public static void main(String[] args) { // 将“对象”通过序列化保存 testWrite(); // 将序列化的“对象”读出来 testRead(); } /** * 将Box对象通过序列化,保存到文件中 */ private static void testWrite() { try { // 获取文件TMP_FILE对应的对象输出流。 // ObjectOutputStream中,只能写入“基本数据”或“支持序列化的对象” ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream(TMP_FILE)); // 创建Box对象 Box box = new Box("desk", 80, 48); // 将box对象写入到对象输出流out中,即相当于将对象保存到文件TMP_FILE中 out.writeObject(box); // 打印“Box对象” System.out.println("testWrite box: " + box); out.close(); } catch (Exception ex) { ex.printStackTrace(); } } /** * 从文件中读取出“序列化的Box对象” */ private static void testRead() { try { // 获取文件TMP_FILE对应的对象输入流。 ObjectInputStream in = new ObjectInputStream( new FileInputStream(TMP_FILE)); // 从对象输入流中,读取先前保存的box对象。 Box box = (Box) in.readObject(); // 打印“Box对象” System.out.println("testRead box: " + box); in.close(); } catch (Exception e) { e.printStackTrace(); } } } /** * Box类实现Externalizable接口 */ class Box implements Externalizable { private int width; private int height; private String name; public Box() { } public Box(String name, int width, int height) { this.name = name; this.width = width; this.height = height; } @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(name); out.writeInt(width); out.writeInt(height); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { name = (String) in.readObject(); width = in.readInt(); height = in.readInt(); } @Override public String toString() { return "["+name+": ("+width+", "+height+") ]"; } }
运行结果:
testWrite box: [desk: (80, 48) ] testRead box: [desk: (80, 48) ]
参考:
http://www.cnblogs.com/skywang12345/p/io_06.html
浙公网安备 33010602011771号