java io进修(五) 序列化总结(Serializable 和 Externalizable)
副标题#e#
本章,我们对序列化举办深入的进修和探讨。进修内容,包罗序列化的浸染、用途、用法,以及对实现序列化的2种方法Serializable和Externalizable的深入研究。
1. 序列化是的浸染和用途
序列化,就是为了生存工具的状态;而与之对应的反序列化,则可以把生存的工具状态再读出来。 简言之:序列化/反序列化,是Java提供一种专门用于的生存/规复工具状态的机制。
一般在以下几种环境下,我们大概会用到序列化: a)当你想把的内存中的工具状态生存到一个文件中可能数据库中时候; b)当你想用套接字在网络上传送工具的时候; c)当你想通过RMI传输工具的时候。
2. 演示措施1
下面,我们先通过一则简朴示例来查察序列化的用法。
源码如下(SerialTest1.java):
/** * 序列化的演示测试措施 * * @author skywang */ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class SerialTest1 { private static final String TMP_FILE = ".serialtest1.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 int width; private 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, 48) ]
#p#副标题#e#
源码说明:
(01) 措施的浸染很简朴,就是演示:先将Box工具,通过工具输出流生存到文件中;之后,再通过工具输入流,将文件中生存的Box工具读取出来。
(02) Box类说明。Box是我们自界说的演示类,它被用于序列化的读写。Box实现了Serialable接口,因此它支持序列化操纵;即,Box支持通过ObjectOutputStream去写入到输出流中,而且支持通过ObjectInputStream从输入流中读取出来。
(03) testWrite()函数说明。testWrite()的浸染就是,新建一个Box工具,然后将该Box工具写入到文件中。 首先,新建文件TMP_FILE的文件输出流工具(即FileOutputStream工具),再建设该文件输出流的工具输出流(即ObjectOutputStream工具)。 a) 关于FileInputStream和FileOutputStream的内容,可以参考“java io系列07之 FileInputStream和FileOutputStream”。 b) 关于ObjectInputStream和ObjectOutputStream的的更多常识,可以参考“java io系列05之 ObjectInputStream 和 ObjectOutputStream” 然后,新建Box工具。 最后,通过out.writeObject(box) 将box写入到工具输出流中。实际上,相当于将box写入到文件TMP_FILE中。
#p#分页标题#e#
(04) testRead()函数说明。testRead()的浸染就是,从文件中读出Box工具。 首先,新建文件TMP_FILE的文件输入流工具(即FileInputStream工具),再建设该文件输入流的工具输入流(即ObjectInputStream工具)。 然后,通过in.readObject() 从工具输入流中读取出Box工具。实际上,相当于从文件TMP_FILE中读取Box工具。
通过上面的示例,我们知道:我们可以自界说类,让它支持序列化(即实现Serializable接口),从而能支持工具的生存/规复。 若要支持序列化,除了“自界说实现Serializable接口的类”之外;java的“根基范例”和“java自带的实现了Serializable接口的类”,都支持序列化。我们通过下面的示例去查察一下。
3. 演示措施2
源码如下(SerialTest2.java):
/** * “根基范例” 和 “java自带的实现Serializable接口的类” 对序列化的支持 * * @author skywang */ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Map; import java.util.HashMap; import java.util.Iterator; public class SerialTest2 { private static final String TMP_FILE = ".serialabletest2.txt"; public static void main(String[] args) { testWrite(); testRead(); } /** * ObjectOutputStream 测试函数 */ private static void testWrite() { try { ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream(TMP_FILE)); out.writeBoolean(true); // 写入Boolean值 out.writeByte((byte)65);// 写入Byte值 out.writeChar('a'); // 写入Char值 out.writeInt(20131015); // 写入Int值 out.writeFloat(3.14F); // 写入Float值 out.writeDouble(1.414D);// 写入Double值 // 写入HashMap工具 HashMap map = new HashMap(); map.put("one", "red"); map.put("two", "green"); map.put("three", "blue"); out.writeObject(map); out.close(); } catch (Exception ex) { ex.printStackTrace(); } } /** * ObjectInputStream 测试函数 */ private static void testRead() { try { ObjectInputStream in = new ObjectInputStream( new FileInputStream(TMP_FILE)); System.out.printf("boolean:%b\n" , in.readBoolean()); System.out.printf("byte:%d\n" , (in.readByte()&0xff)); System.out.printf("char:%c\n" , in.readChar()); System.out.printf("int:%d\n" , in.readInt()); System.out.printf("float:%f\n" , in.readFloat()); System.out.printf("double:%f\n" , in.readDouble()); // 读取HashMap工具 HashMap map = (HashMap) in.readObject(); Iterator iter = map.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = (Map.Entry)iter.next(); System.out.printf("%-6s -- %s\n" , entry.getKey(), entry.getValue()); } in.close(); } catch (Exception e) { e.printStackTrace(); } } }
运行功效:
boolean:true byte:65 char:a int:20131015 float:3.140000 double:1.414000 two -- green one -- red three -- blue
源码说明:
(01) 措施的浸染很简朴,就是演示:先将“根基范例数据”和“HashMap工具”,通过工具输出流生存到文件中;之后,再通过工具输入流,将这些生存的数据读取出来。
(02) testWrite()函数说明。testWrite()的浸染就是,先将“根基范例数据”和“HashMap工具”,通过工具输出流生存到文件中。 首先,新建文件TMP_FILE的文件输出流工具(即FileOutputStream工具),再建设该文件输出流的工具输出流(即ObjectOutputStream工具)。 然后,通过 writeBoolean(), writeByte(), … , writeDouble() 等一系列函数将“Boolean, byte, char, … , double等根基数据范例”写入到工具输出流中。实际上,相当于将这些内容写入到文件TMP_FILE中。 最后,新建HashMap工具map,并通过out.writeObject(map) 将map写入到工具输出流中。实际上,相当于map写入到文件TMP_FILE中。 关于HashMap的更多常识,可以参考“Java 荟萃系列10之 HashMap具体先容(源码理会)和利用示例”。
#p#分页标题#e#
(03) testRead()函数说明。testRead()的浸染就是,从文件中读出testWrite()写入的工具。 首先,新建文件TMP_FILE的文件输入流工具(即FileInputStream工具),再建设该文件输入流的工具输入流(即ObjectInputStream工具)。 然后,通过in.readObject() 从工具输入流中读取出testWrite()工具。实际上,相当于从文件TMP_FILE中读取出这些工具。
在前面,我们提到过:若要支持序列化,除了“自界说实现Serializable接口的类”之外;java的“根基范例”和“java自带的实现了Serializable接口的类”,都支持序列化。为了验证这句话,我们看看HashMap是否实现了Serializable接口。 HashMap是java.util包中界说的类,它的接口声明如下:
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {}
至此,我们对序列化的认识已经较量深入了:即知道了“序列化的浸染和用法”,也知道了“根基范例”、“java自带的支持Serializable接口的类”和“自界说实现Serializable接口的类”都能支持序列化。 应付序列化的简朴利用应该足够了。可是,我们的目标是对序列化有更深条理的相识!更况且,写此文的作者(也就是区区在下),应该比列位看官要累(既要写代码,又要总结,还得留意排版和用词,讲的通俗易懂,让列位看得轻松自在);我这个菜鸟都能做到这些,况且对常识极其盼愿的您呢?所以,请深吸一口吻,然后继承……
我们在先容序列化界说时,说过“序列化/反序列化,是专门用于的生存/规复工具状态的机制”。 从中,我们知道:序列化/反序列化,只支持生存/规复工具状态,即仅支持生存/规复类的成员变量,但不支持生存类的成员要领! 可是,序列化是不是对类的所有的成员变量的状态都能生存呢? 谜底虽然是否认的! (01) 序列化对static和transient变量,是不会自动举办状态生存的。 transient的浸染就是,用transient声明的变量,不会被自动序列化。 (02) 对付Socket, Thread类,不支持序列化。若实现序列化的接口中,有Thread成员;在对该类举办序列化操纵时,编译会堕落! 这主要是基于资源分派方面的原因。假如Socket,Thread类可以被序列化,可是被反序列化之后也无法对他们举办从头的资源分派;再者,也是没有须要这样实现。
下面,我们照旧通过示例来查察“序列化对static和transient的处理惩罚”。
4. 演示措施3
我们对前面的SerialTest1.java举办简朴修改,获得源文件(SerialTest3.java)如下:
/** * 序列化的演示测试措施 * * @author skywang */ 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+") ]"; } }
SerialTest3.java 对比于 SerialTest1.java。仅仅对Box类中的 width 和 height 变量的界说举办了修改。 SerialTest1.java 中width和height界说
private int width; private int height;
SerialTest3.java 中width和height界说
查察本栏目
private static int width; private transient int height;
在看后头的功效之前,我们发起各人对措施举办阐明,先本身得出一个结论。
运行功效:
testWrite box: [desk: (80, 48) ] testRead box: [desk: (80, 0) ]
功效阐明:
#p#分页标题#e#
我们前面说过,“序列化差池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。
领略上面的内容之后,我们应该可以揣度出下面的代码的运行功效。
源码如下(SerialTest4.java):
/** * 序列化的演示测试措施 * * @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+") ]"; } }
SerialTest4.java 对比于 SerialTest3.java,在testWrite()中添加了一行代码box = new Box("room", 100, 50);
运行功效:
testWrite box: [desk: (80, 48) ] testRead box: [desk: (100, 0) ]
#p#分页标题#e#
此刻,我们越发确认“序列化差池static和transient变量举办状态生存”。可是,若我们想要生存static或transient变量,能不能办到呢? 虽然可以!我们在类中重写两个要领writeObject()和readObject()即可。下面措施演示了如何手动生存static和transient变量。
5. 演示措施4
我们对前面的SerialTest4.java举办简朴修改,以到达:序列化存储static和transient变量的目标。
源码如下(SerialTest5.java):
/** * 序列化的演示测试措施 * * @author skywang */ 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: (100, 0) ]
措施说明:
“序列化不会自动生存static和transient变量”,因此我们若要生存它们,则需要通过writeObject()和readObject()去手动读写。 (01) 通过writeObject()要领,写入要生存的变量。writeObject的原始界说是在ObjectOutputStream.java中,我们凭据如下示例包围即可:
#p#分页标题#e#
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() }
至此,我们就先容完了“序列化对static和transient变量的处理惩罚”。 接下来,我们来研究“对付Socket, Thread类,不支持序列化”。照旧通过示例来查察。
6. 演示措施5
我们修改SerialTest5.java的源码,在Box类中添加一个Thread成员。
源码如下(SerialTest6.java):
/** * 序列化的演示测试措施 * * @author skywang */ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.Thread; import java.io.IOException; import java.lang.ClassNotFoundException; public class SerialTest6 { private static final String TMP_FILE = ".serialtest6.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; private Thread thread = new Thread() { @Override public void run() { System.out.println("Serializable thread"); } }; 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+") ]"; } }
功效是,编译堕落!
#p#分页标题#e#
事实证明,不能对Thread举办序列化。若但愿措施能编译通过,我们对Thread变量添加static或transient修饰即可!如下,是对Thread添加transient修饰的源码(SerialTest7.java):
/** * 序列化的演示测试措施 * * @author skywang */ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.Thread; import java.io.IOException; import java.lang.ClassNotFoundException; public class SerialTest7 { private static final String TMP_FILE = ".serialtest7.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; private transient Thread thread = new Thread() { @Override public void run() { System.out.println("Serializable thread"); } }; 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+") ]"; } }
至此,关于“Serializable接口”来实现序列化的内容,都说完了。为什么这么说?因为,实现序列化,除了Serializable之外,尚有其它的方法,就是通过实现Externalizable来实现序列化。整理下脸色,下面继承对Externalizable举办相识。
7. Externalizable和完全定制序列化进程
假如一个类要完全认真本身的序列化,则实现Externalizable接口,而不是Serializable接口。
#p#分页标题#e#
Externalizable接口界说包罗两个要领writeExternal()与readExternal()。需要留意的是:声明类实现Externalizable接口会有重大的安详风险。writeExternal()与readExternal()要领声明为public,恶意类可以用这些要领读取和写入工具数据。假如工具包括敏感信息,则要分外小心。
下面,我们修改之前的SerialTest1.java测试措施;将个中的Box由“实现Serializable接口” 改为 “实现Externalizable接口”。 修改后的源码如下( ExternalizableTest1.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 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