序列化与反序列化
来源:互联网 发布:php服务器程序招聘 编辑:程序博客网 时间:2024/05/21 11:04
序列化基础:即使用ObjectOutputStream与ObjectInputStream进行对象与字节流的转换,一般需要提供一个序列化id。tip:默认序列化时若一个域被修饰为transient,则不序列化该实例域。import java.io.*;public class Test { public static void main(String[] args) throws Exception{ //将两个对象序列化存储到文件中 File f = new File("oos.txt"); System.out.println(f.exists()); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(f)); oos.writeObject(new T(1)); oos.writeObject(new T(2)); oos.close(); //从序列化文件反序列化生成两个对象 ObjectInputStream ois = new ObjectInputStream(new FileInputStream(f)); T t1 = (T)ois.readObject(); T t2 = (T)ois.readObject(); t1.get(); t2.get(); ois.close(); }}class T implements Serializable { private int x; public T(int x){ this.x = x; } public void get(){ System.out.println(x); } /** * Serilizable接口没有抽象方法,所以以下四个方法可以不写 *以下四个方法,为自定义序列化时的可选方法,将由ObjectOutputStream * 与ObjectInputStream进行反射调用。 */ //此方法在写入序列化文件时最先被调用,其返回一个Serilizable对象用于代替当前对象进行序列化 private Object writeReplace(){ return new T(5); } //此方法用于选择保存当前对象的关键域(决定这个对象的实例域)到序列化文件 private void writeObject(ObjectOutputStream os) throws Exception { //为了往后兼容 os.defaultWriteObject(); os.writeInt(x); } //此方法用于从序列化文件中获取数据用来恢复关键域 private void readObject(ObjectInputStream is) throws Exception{ //为了往后兼容 is.defaultReadObject(); x = is.readInt(); } //此方法在恢复对象时最后被调用,其返回一个对象用于替代文件恢复的对象,一般用于序列化代理 private Object readResolve(){ return new T(4); }}序列化高级:谨慎地实现Serilizable接口,其代价如下一旦类被公布,就降低了修改这个类的可能性增加了bug和可能问题,可能破坏singleton模式测试负担增加考虑自定义的序列化形式考虑以下的StringList类,若使用默认的自定义形式,其将对head进行序列化,因此对链表的每个节点进行序列化,一来,增大了序列化的大小;二来,使得字符串列表限制只能使用链表Entry实现;三来,增大了序列化时间,其将对previous与next均进行序列化,需要有昂贵的图遍历过程,而我们可以简单调用next获得字符串列表;四来,在元素多时,递归序列化可能造成栈溢出。import java.io.Serializable;/** * Created by Doggy on 2015/9/13. */public final class StringList implements Serializable{ private int size = 0; private Entry head = null; private static class Entry implements Serializable{ private Entry previous; private Entry next; private String value; }}因为对于字符串列表来说只关心字符串个数与顺序,所以可以采用以下自定义的序列化方法代替,自定义序列化时大部分实例域应该被标记为transient(一个域被声明为transient,则其反序列化的值对于int为0,引用则为null,直到执行readObject才会初始化)编写一个线程安全的可序列化类需要对readObject以及writeObject加锁import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;/** * Created by Doggy on 2015/9/13. */public final class StringList implements Serializable{ //修饰为transient避免默认序列化时序列化该实例域 private transient int size = 0; private transient Entry head = null; //添加一个增加字符串的方法 private final void addOne(String s){ Entry ent = new Entry(); ent.value = s; Entry tmp = head; while(tmp.next != null){ tmp = tmp.next; } ent.previous = tmp; tmp.next = ent; } private static class Entry implements Serializable{ private Entry previous; private Entry next; private String value; } //编写writeObject进行自定义序列化 private void writeObject(ObjectOutputStream os) throws Exception{ //为了向后拓展,后期在类中加入一些实例域可能有用 os.defaultWriteObject(); //写入字符串列表的大小 os.writeInt(size); //将字符串列表中的每个字符串按顺序写入文件 while(tmp.next != null){ os.writeObject(tmp.value); tmp = tmp.next; } } private void readObject(ObjectInputStream is) throws Exception{ //为了向后拓展,后期在类中加入一些实例域可能有用 is.defaultReadObject(); //读取大小到对象中 size = is.readInt(); //根据列表元素以及addOne方法进行恢复 for (int i = 0; i < size; i++) { addOne((String)is.readObject()); } }}保护性地编写readObject方法/* *readObject应该与构造器类似,不能(间接)调用一个可覆盖的方法 * 且应该实现与构造器一致的有效性检测与保护性拷贝(防止内部实例域引用泄露) */ private void readObject(ObjectInputStream is) throws Exception{ //为了向后拓展,后期在类中加入一些实例域可能有用 is.defaultReadObject(); //保护性拷贝,若不实现,则可能通过伪造字节流,获得对start与end的引用,在客户端修改该类的start、end域,影响类的不可变性 start = new Date(start.getTime()); end = new Date(end.getTime()); //数据有效性检测 if(start.compareTo(end) > 0){ throw new InvalidObjectException(); } }枚举单例优先于使用readResolve控制的序列化单例//可以在readResolve中直接返回单例对象,但所有实例域必须被声明为transient//否则在未执行readResolve之前的readObject产生的新单例对象可能被盗用。private Object readResolve(){ return INSTANCE;}考虑使用序列化代理代替序列化实例/** * 好处是外部类的所有实例都是从构造器创建, * 所以可以防止以上的伪造流以及盗用者造成的危害 * 也不用特别检测数据的有效性,因为在构造器中已经检测过 */class Period{ private final Date start; private final Date end; public Period(Date start,Date end){ this.start = new Date(start.getTime()); this.end = new Date(end.getTime()); //数据有效性检测 if(start.compareTo(end) > 0){ throw new InvalidParameterException(); } } //使用writeReplace将序列化任务转发给代理,所以不存在任何外部类的序列化实例 private Object writeReplace(){ return new PeriodProxy(start,end); } //防止对外部类使用字节流创建对象,直接对伪造流抛异常 private void readObject(){ throw new InvalidObjectException(); } //这里要是static,否则调用defaultWriteObject时,会将类信息写入,导致读异常。 private static class PeriodProxy{ private final Date start; private final Date end; private PeriodProxy(Date start,Date end){ this.start = start; this.end = end; } //自定义实现readObject和writeObject // ... //实现writeResolve,将内部代理转换为外部类对象 private Object readResolve(){ //若为单例则直接返回INSTANCE return new Period(start,end); } }}
0 0
- 序列化与反序列
- 序列化与反序列
- XML序列化与反序列化
- 对象序列化与反序列化
- 序列化与反序列化
- 转载:序列化与反序列化
- c#序列化与反序列化
- java序列化与反序列化
- XML序列化与反序列化
- 序列化与反序列化
- 序列化与反序列化
- C#序列化与反序列化
- java序列化与反序列化
- XML序列化与反序列化
- 再看序列化与反序列化
- 序列化与反序列化对象
- 序列化与反序列化
- .NET序列化与反序列化
- CentOS 卸载OpenJdk
- Linux环境之进程调度算法
- NIO实例
- Android中asyncTask与handler的区别
- tjut 5299
- 序列化与反序列化
- SpringMVC之TCP协议三次握手过程分析(二)
- SICP 习题1.46 iterative-improve
- Spring配置声明式事务--注解方式
- 归并排序(初步)
- js学习笔记(构造器对象、原型对象)
- 框架 day50 BOS项目 4 批量导入(ocupload插件,pinyin4J)/POI解析Excel/Combobox下拉框/分区组合条件分页查询(ajax)/分区数据导出(Excel)
- Java中的static关键字解析
- 程序员福利:各大平台免费接口,非常实用