Java基础-IO流对象之序列化(ObjectOutputStream)与反序列化(ObjectInputStream)

                                          作者:尹正杰

版权声明:原创作品,谢绝转载!否则将追究法律责任。

一.对象的序列化与反序列化

  ObjectOutputStream流用于将对象保存在磁盘中,或者通过网络传输到另一台主机上。保存在文件中的对象的二进制流可以用ObjectInputStream流在以后被还原成原来的对象。

  对象输出流的对象可以永久的保存在磁盘上,使对象可以脱离程序而存在,此过程也称为“序列化”过程,反之,将磁盘的数据加载到内存中的就称为“反序列化”过程。换句话说,对象中的数据以流的形式写入到文件中保存的过程称为写出对象,也叫对象的序列化,在文件中以流的形式将对象读取出来,读取对象的过程也叫反序列化。

  

  注意:反序列化是不走构造方法的哟!

二.ObjectOutputStream流写对象

  类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。

  接下来我们定义一个实现Serializable类接口如下:

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/ package cn.org.yinzhengjie.note6; import java.io.Serializable; public class Person implements Serializable{
private String Name;
private int Age;
public Person(String name, int age) {
super();
Name = name;
Age = age;
} public String getName() {
return Name;
} public void setName(String name) {
Name = name;
} public int getAge() {
return Age;
} public void setAge(int age) {
Age = age;
} @Override
public String toString() {
return "Person [姓名=" + Name + ",年龄=" + Age+ "]";
}
}

Person.java 文件内容

  定义好需要序列化的对象之后,我们需要就来搞事情吧,看看ObjectOutputStream 到底是如何使用的,案例如下:

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/ package cn.org.yinzhengjie.note6; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream; public class ObjectOutputStreamDemo {
public static void main(String[] args) throws IOException {
//创建字节输出流,封装文件
File file = new File("yinzhengjie.txt");
if(!file.exists()) {
file.createNewFile();
}
FileOutputStream fos = new FileOutputStream(file); //创建写出对象的序列化流的对象,构造方法传递字节输出流
ObjectOutputStream oos = new ObjectOutputStream(fos);
Person p = new Person("yinzhengjie", 18);
//调用序列化流的方法writeObject,写出对象。需要实例对象p具有序列化接口,否则会抛出异常对象:java.io.NotSerializableException
oos.writeObject(p);
//别忘了释放资源哟!
oos.close();
}
}

三.ObjectInputStream流读取对象

  在反序列一个文件内容的时候可能会存在java.lang.ClassNotFoundException类异常,原因是缺少反序列的字节码(*.class)文件。因此,序列化的前提是:必须有反序列化相关的字节码文件。现在我们把之前序列化的文件进行反序列操作,如下:

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/ package cn.org.yinzhengjie.note6; import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream; public class ObjectInputSteamDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//创建字节输入流,封装文件
File file = new File("yinzhengjie.txt");
FileInputStream fis = new FileInputStream(file);
//创建反序列化流,构造方法中,传递字节输入流
ObjectInputStream ois = new ObjectInputStream(fis);
//调用反序列化流的方法"readObject()"读取对象,要注意的是反序列话的对象需要存在相应的字节码文件。否则会抛异常
Object obj = ois.readObject();
//记得释放资源
ois.close();
//查看我们的想要看的内容。
System.out.println(obj);
}
} /*
以上代码执行结果如下:
Person [姓名=yinzhengjie,年龄=18]
*/

  注意,在序列化和反序列化过程中,序列化用的什么方法写入,我们读取的时候也应该用对应的方法去读取,打个比方,我们上面序列化一个自定义类时,用的是wirteObject()方法,读取的时候应该用对应方法读取,我们上面的案例就是用readObject方法进行读取,如果你序列化使用的wirteInt()方法,那么反序列话的时候就应该用readInt()方法哟!谨记这一点,会让你少踩很多坑的,别问我为什么这么说,因为这个坑我已经踩了你就别去踩了哈!

四.关于序列化的面试题

1>.静态成员变量为什么不能被序列化?

  答:序列化其实是将对象进行序列化操作,而static修饰的成员变量是属于类的,并非对象所有!因此静态修饰的成员变量无法被序列化。

2>.瞬态关键字transient的用法?

  答:当一个类的对象需要被序列化时,某些属性不需要被反序列化,这时不需要序列化的属性可以使用关键字transient修饰,只要被transient修饰了,序列化时这个属性就不会被序列化啦!它的用法比较单一,只能用于修饰成员变量不被序列化!这样做的目的是可以节省空间,将不需要的数据不进行序列化操作。

3>.Serializable接口有上面含义?

  答:Serializable并没有任何功能,只是一个标记性接口,就好像去菜市场买猪肉,安检人员会在猪肉上印上一个标记表示该猪肉检验合格可以食用!而在Java中用该接口只是标识该类是可以被序列化!

4>.分析序列化中的为什么会存在序列化冲突问题?

  答:Java代码在执行之前需要经过javac命令对源代码进行编译生成字节码文件“*.class”,与此同时会给该“*.class”计算出来一个序列号(serialVersionUID),在序列化时会将该属性一并序列化到文件中。当我们对源代码再次进行编辑时,依然是需要javac命令进行编译该文件才能运行修改后的代码,此时会生成一个新的序列号(serialVersionUID)出来。这个时候当我们将序列化的文件进行反序列化操作时,首先会对比字节码文件的序列号是否一致,如果不一致,则会抛出异常:“java.io.InvalidClassException”。

5>.为什么要自定义序列号?

  答:原因很简单,就是为了解决序列化冲突问题。我们只需要要让源代码修改前和修改后的序列号(serialVersionUID)保持一致,这样就可以正常进行序列化操作啦!我们可以看一下案例如下:

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/ package cn.org.yinzhengjie.note6; import java.io.Serializable; public class Person implements Serializable{
private String Name;
public /*transient关键字在这里加,就可以阻止成员变量进行序列化啦!*/int Age; static final long serialVersionUid = 7758520L; //类自定义序列号,编译器就不会计算序列号。 public Person(String name, int age) {
super();
Name = name;
Age = age;
} public String getName() {
return Name;
} public void setName(String name) {
Name = name;
} public int getAge() {
return Age;
} public void setAge(int age) {
Age = age;
} @Override
public String toString() {
return "Person [姓名=" + Name + ",年龄=" + Age+ "]";
} }

五.小试牛刀

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.serializable; import java.io.Serializable;
import java.util.ArrayList;
import java.util.List; /**
* 客户类
*/
public class Customer implements Serializable {
private static final long serialVersionUID = 6327665722505706622L;
private String name ;
private int age ; public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} //临时的,不参与串行化
private transient List<Order> orders = new ArrayList<Order>() ; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public List<Order> getOrders() {
return orders;
} public void setOrders(List<Order> orders) {
this.orders = orders;
}
}

Customer.java 文件内容

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.serializable; import java.io.Serializable; /**
* 客户类
*/
public class Item implements Serializable {
private String itemname; public String getItemname() {
return itemname;
} public void setItemname(String itemname) {
this.itemname = itemname;
}
}

Item.java 文件内容

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.serializable; import java.io.Serializable;
import java.util.ArrayList;
import java.util.List; /**
* 客户类
*/
public class Order implements Serializable {
private String orderno; private List<Item> items =new ArrayList<Item>() ; public List<Item> getItems() {
return items;
} public void setItems(List<Item> items) {
this.items = items;
} public String getOrderno() {
return orderno;
} public void setOrderno(String orderno) {
this.orderno = orderno;
}
}

Order.java 文件内容

1>.实现浅拷贝

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.serializable; import org.junit.Test; public class ShallowReplicas {
@Test
public void testDeeplyCopy() throws Exception {
Customer c = new Customer();
c.setName("尹正杰"); Order o1 = new Order();
o1.setOrderno("0001"); Order o2 = new Order();
o2.setOrderno("0002"); Item i1 = new Item();
i1.setItemname("iphone 8 Plus"); //建立关系
c.getOrders().add(o1);
c.getOrders().add(o2); o1.getItems().add(i1); //浅度复制
Customer cc = new Customer();
cc.setName(c.getName());
cc.setOrders(c.getOrders()); String name = cc.getName();
System.out.println(name);
}
}

2>.实现深度拷贝

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/ package cn.org.yinzhengjie.serializable; import org.junit.Test; import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; public class DeepCopy {
String filePath = "D:\\BigData\\JavaSE\\yinzhengjieData\\c.dat"; /**
* 深度拷贝,将对象序列化到文件
*/
@Test
public void testDeeplyCopy() throws Exception {
Customer c = new Customer();
c.setName("尹正杰"); Order o1 = new Order();
o1.setOrderno("0001"); Order o2 = new Order();
o2.setOrderno("0002"); Item i1 = new Item();
i1.setItemname("iphone 8 Plus"); //建立关系
c.getOrders().add(o1);
c.getOrders().add(o2); o1.getItems().add(i1); //深度拷贝,将对象序列化到文件
FileOutputStream fos = new FileOutputStream(filePath) ;
ObjectOutputStream oos = new ObjectOutputStream(fos) ;
oos.writeObject(c);
oos.close();
fos.close();
System.out.println();
} /**
* 定义反序列化的代码
*/
@Test
public void testDeserialize() throws Exception {
FileInputStream fis = new FileInputStream(filePath);
ObjectInputStream ois = new ObjectInputStream(fis);
Customer obj = (Customer)ois.readObject();
ois.close();
fis.close();
System.out.println(obj.getName());
}
} /*
以上代码执行结果如下:
尹正杰
*/

最新文章

  1. centos编译安装apache
  2. 经典的找不到符号(symbol)错误 #iOS开发
  3. Java多线程8:wait()和notify()/notifyAll()
  4. 初探Ajax
  5. jQuery对象和DOM对象之间的相互转换
  6. java调用http请求json
  7. HDu 3449 (有依赖的01背包) Consumer
  8. 很牛逼的android真机调试,手机、平板、电视都可
  9. C3P0连接池配置方式
  10. 关于使用mybatis的几点总结
  11. String to Integer (atoi) - 字符串转为整形,atoi 函数(Java )
  12. [2013-01-15]The Little Schemer 学习笔记
  13. ACM hdu 3336 Count the string
  14. 支持scrollTo的RecycleView
  15. Java 集合系列(四)—— ListIterator 源码分析
  16. 队列优化的dijkstra
  17. PDF 补丁丁 0.6.0.3427 版发布(修复提取图片问题)
  18. python 链表表达式 map、filter易读版
  19. windows 下安装 docker
  20. JavaMail直接发送邮件

热门文章

  1. [BUAA软工]第零次博客作业---问题回答
  2. 软工1816 &#183; 作业(十二)Beta答辩总结
  3. c++工厂模式(Factory method)
  4. 团队作业7——第二次项目冲刺(Beta版本12.07——12.08)
  5. vue开发完成后打包后图片路径不对
  6. PHP ini_set
  7. Linux进程调度策略的发展和演变(转)
  8. ionic2如何升级到最新版本、配置开发环境
  9. Multiple Instance Learning
  10. BZOJ3504 CQOI2014危桥(最大流)