首页 / JAVA / Java对象序列化与反序列化
Java对象序列化与反序列化
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了Java对象序列化与反序列化,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含9619字,纯文字阅读大概需要14分钟。
内容图文
![Java对象序列化与反序列化](/upload/InfoBanner/zyjiaocheng/1229/9be425d8a59045d8b38feefbee6b44ea.jpg)
对象序列化的目标是将对象保存在磁盘中或者在网络中进行传输。实现的机制是允许将对象转为与平台无关的二进制流。
java中对象的序列化机制是将允许对象转为字节序列。这些字节序列可以使Java对象脱离程序存在,从而可以保存在磁盘上,也可以在网络间传输。
对象的序列化是将一个Java对象写入IO流;与此对应的,反序列化则是从IO流中恢复一个Java对象。
实现序列化
如果要将一个java对象序列化,那么对象的类需要是可序列化的。要让类可序列化,那么这个类需要继承如下两个接口:
- Serializable
- Externalizable
使用Serializable序列化
实现Serializable接口非常简单,只要让java实现Serializable接口即可,无需实现任何方法。
一个类一旦实现了Serializable接口,那么该类的对象就是可序列化的。实现类的对象的序列化可以使用ObjectOutputStream,实现步骤如下:
- 创建ObjectOutputStream对象;
- 调用ObjectOutputStream的writeObject方法输出对象。
以下是一个实例:
package com.zhyea.test; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.Serializable; /** * 序列化测试类 * * @author robin * @date 2014年12月18日 */ public class SerialTest { public static void main(String[] args) { ObjectOutputStream oos = null; try { oos = new ObjectOutputStream(new FileOutputStream("D:\\object.txt")); Person robin = new Person("robin", 29); oos.writeObject(robin); } catch (IOException e) { e.printStackTrace(); } finally { if (null != oos) { try { oos.close(); } catch (IOException e) { e.printStackTrace(); } } } } } /** * 序列化测试用对象 * * @author robin * @date 2014年12月18日 */class Person implements Serializable{ privatestaticfinallong serialVersionUID = -6412852654889352693L; /** * 姓名 */private String name; /** * 年龄 */privateint age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } publicvoid setName(String name) { this.name = name; } publicint getAge() { return age; } publicvoid setAge(int age) { this.age = age; } }
如上的代码实现了将一个Person对象保存在了磁盘的一个文本文件object.txt上。运行程序在D盘上生成了一个object.txt文件。以下是文件内容:
有乱码(字节流转字符流导致的),但仍不影响我们分辨出里面是不是我们保存的对象。
接下来需要反序列化将Person对象从磁盘上读出。相应的反序列化需要使用的类是ObjectInputStream,反序列化步骤如下:
- 创建ObjectInputStream对象;
- 使用ObjectInputStream的readObject方法取出对象。
接下来,重构下我们的代码,实现反序列化,如下:
package com.zhyea.test; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; /** * 序列化测试类 * * @author robin * @date 2014年12月18日 */ public class SerialTest { public static void main(String[] args) { Person robin = new Person("robin", 29); String savePath = "D:\\object.txt"; SerialTest test = new SerialTest(); try { test.serialize(robin, savePath); Person person = (Person) test.deSerialize(savePath); System.out.println("Name:" + person.getName() + " Age:" + person.getAge()); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } /** * 实现序列化 * * @param obj * 要被序列化保存的对象 * @param path * 保存地址 * @throws IOException */publicvoid serialize(Object obj, String path) throws IOException { ObjectOutputStream oos = null; try { oos = new ObjectOutputStream(new FileOutputStream(path)); oos.writeObject(obj); } finally { if (null != oos) oos.close(); } } /** * 反序列化取出对象 * * @param path * 被序列化对象保存的位置 * @return * @throws IOException * @throws ClassNotFoundException */public Object deSerialize(String path) throws IOException, ClassNotFoundException { ObjectInputStream ois = null; try { ois = new ObjectInputStream(new FileInputStream(path)); return ois.readObject(); } finally { if (null != ois) ois.close(); } } } /** * 序列化测试用对象 * * @author robin * @date 2014年12月18日 */class Person implements Serializable { privatestaticfinallong serialVersionUID = -6412852654889352693L; /** * 姓名 */private String name; /** * 年龄 */privateint age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } publicvoid setName(String name) { this.name = name; } publicint getAge() { return age; } publicvoid setAge(int age) { this.age = age; } }
关于对象序列化与反序列化还有几点需要注意:
- 反序列化无需通过构造器初始化对象;
- 如果使用序列化机制向文件中写入了多个对象,那么取出和写入的顺序必须一致;
- Java对类的对象进行序列化时,若类中存在对象引用(且值不为null),也会对类的引用对象进行序列化。
使用transient
在一些特殊场景下,比如银行账户对象,出于保密考虑,不希望对存款金额进行序列化。或者类的一些引用类型的成员是不可序列化的。此时可以使用transient关键字修饰不想被或者不能被序列化的成员变量。
继续调整我们的代码来做演示:
package com.zhyea.test; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; /** * 序列化测试类 * * @author robin * @date 2014年12月18日 */ public class SerialTest { public static void main(String[] args) { Person robin = new Person("robin", 29); School school = new School("XX学校"); Teacher tRobin = new Teacher(robin); tRobin.setSchool(school); tRobin.setSalary(12.0); String savePath = "D:\\object.txt"; SerialTest test = new SerialTest(); try { test.serialize(savePath, tRobin); Teacher t = (Teacher) test.deSerialize(savePath); System.out.println("Name:" + t.getPerson().getName() +" Salary:" + t.getSalary()); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } /** * 实现序列化 * * @param obj * 要被序列化保存的对象 * @param path * 保存地址 * @throws IOException */publicvoid serialize(String path, Object ... obj) throws IOException { .... } /** * 反序列化取出对象 * * @param path * 被序列化对象保存的位置 * @return * @throws IOException * @throws ClassNotFoundException */public Object deSerialize(String path) throws IOException, ClassNotFoundException { ... } } /** * Teacher类 * @author robin * @date 2014年12月18日 */class Teacher implements Serializable{ privatestaticfinallong serialVersionUID = -8751853088437904443L; private Person person; privatetransient School school; privatetransientdouble salary; public Teacher(Person person){ this.person = person; } /*略去get、set,请自行补充*/ } /** * School类,不可序列化 * * @author robin * @date 2014年12月18日 */class School{ private String name; public School(String name){ this.name = name; } /*略去get、set,请自行补充*/ } /** * Person类,可序列化 * * @author robin * @date 2014年12月18日 */class Person implements Serializable { .... }
在不对Teacher类的school成员添加transient标识的情况下,若school值不为null,会报NotSerializableException。异常信息如下:
在不对Teacher类的salary成员添加transient标识的时候,会如实输出salary的值,添加后则只会输出salary的默认初始值即0.0。
需要注意的是transient只能修饰属性(filed),不能修饰类或方法。
自定义序列化
transient提供了一种简洁的方式将被transient修饰的成员属性完全隔离在序列化机制之外。这样子固然不错,但是Java还提供了一种自定义序列化机制让开发者更自由地控制如何序列化各个成员属性,或者不序列化某些属性(与transient效果相同)。
在需要自定义序列化和反序列化的类中需要提供以下方法:
- private void writeObject(ObjectOutputStream out)
- private void readObject(ObjectInputStream in)
- private void readObjectNoData()
先说下前两个方法writeObject和readObject,这两个方法和ObjectOutputStream及ObjectInputStream里对应的方法名称相同。实际上,尽管这两个方法是private型的,但是仍然是在被序列化(或反序列化)阶段被外部类ObjectOutputStream(或ObjectInputStream)调用。仅以序列化为例,ObjectOutputStream在执行自己的writeObject方法前会先通过反射在要被序列化的对象的类中(有点绕口是吧)查找有无自定义的writeObject方法,如有的话,则会优先调用自定义的writeObject方法。因为查找反射方法时使用的是getPrivateMethod,所以自定以的writeObject方法的作用域要被设置为private。通过自定义writeObject和readObject方法可以完全控制对象的序列化与反序列化。
如下是示例代码:
package com.zhyea.test; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import com.sun.xml.internal.ws.encoding.soap.DeserializationException; /** * 序列化测试类 * * @author robin * @date 2014年12月18日 */ public class SerialTest { public static void main(String[] args) { Person robin = new Person("robin", 29); String savePath = "D:\\object.txt"; SerialTest test = new SerialTest(); try { test.serialize(savePath, robin); Person person = (Person) test.deSerialize(savePath); System.out.println("Name:" + person.getName() +" Age:" + person.getAge()); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } /** * 实现序列化 * * @param obj * 要被序列化保存的对象 * @param path * 保存地址 * @throws IOException */publicvoid serialize(String path, Person ... obj) throws IOException { ObjectOutputStream oos = null; ... } /** * 反序列化取出对象 * * @param path * 被序列化对象保存的位置 * @return * @throws IOException * @throws ClassNotFoundException */public Object deSerialize(String path) throws IOException, ClassNotFoundException { ... } } /** * Person类,可序列化 * * @author robin * @date 2014年12月18日 */class Person implements Serializable { privatestaticfinallong serialVersionUID = -6412852654889352693L; /** * 姓名 */private String name; /** * 年龄 */privateint age; public Person() {} public Person(String name, int age) { this.name = name; this.age = age; } /* 略去get和set,请自行实现 */privatevoid writeObject(ObjectOutputStream out) throws IOException{ out.writeObject(name); out.writeInt(age + 1); System.out.println("my write"); } privatevoid readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{ this.name = "zhangsan"; this.age = 30; System.out.println("my read"); } }
以下是输出结果:
关于readObjectNoData,在网上找了如下一段说明:
readObjectNoData 原始情况 pojo public class Person implements Serializable { privateint age; public Person() { } //setter getter... } 序列化 Person p = new Person(); p.setAge(10); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("c:/person.ser")); oos.writeObject(p); oos.flush(); oos.close(); 类结构变化后, 序列化数据不变 pojo Animal implements Serializable 显式编写readObjectNoData publicclass Animal implements Serializable { private String name; public Animal() { } //setter getter... privatevoid readObjectNoData() { this.name = "zhangsan"; } } Person extends Animal publicclass Person extends Animal implements Serializable { privateint age; public Person() { } // setter getter... } 反序列化 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("c:/person.ser")); Person sp = (Person) ois.readObject(); System.out.println(sp.getName()); readObject时, 会调用readObjectNoData
readObjectNoData在我理解看来像是一种异常处理机制,用来在序列化的流不完整的情况下返回正确的值。
writeReplace和readResolve
原文:http://www.cnblogs.com/amunote/p/4171799.html
内容总结
以上是互联网集市为您收集整理的Java对象序列化与反序列化全部内容,希望文章能够帮你解决Java对象序列化与反序列化所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。