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());
}
} /*
以上代码执行结果如下:
尹正杰
*/

Java基础-IO流对象之序列化(ObjectOutputStream)与反序列化(ObjectInputStream)的更多相关文章

  1. Java基础-IO流对象之压缩流(ZipOutputStream)与解压缩流(ZipInputStream)

    Java基础-IO流对象之压缩流(ZipOutputStream)与解压缩流(ZipInputStream) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 之前我已经分享过很多的J ...

  2. Java基础-IO流对象之随机访问文件(RandomAccessFile)

    Java基础-IO流对象之随机访问文件(RandomAccessFile) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.RandomAccessFile简介 此类的实例支持对 ...

  3. Java基础-IO流对象之内存操作流(ByteArrayOutputStream与ByteArrayInputStream)

    Java基础-IO流对象之内存操作流(ByteArrayOutputStream与ByteArrayInputStream) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.内存 ...

  4. Java基础-IO流对象之数据流(DataOutputStream与DataInputStream)

    Java基础-IO流对象之数据流(DataOutputStream与DataInputStream) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.数据流特点 操作基本数据类型 ...

  5. Java基础-IO流对象之打印流(PrintStream与PrintWriter)

    Java基础-IO流对象之打印流(PrintStream与PrintWriter) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.打印流的特性 打印对象有两个,即字节打印流(P ...

  6. java基础-IO流对象之Properties集合

    java基础-IO流对象之Properties集合 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Properties集合的特点 Properties类表示了一个持久的属性集. ...

  7. Java基础-IO流对象之字符缓冲流(BufferedWriter与BufferedReader)

    Java基础-IO流对象之字符缓冲流(BufferedWriter与BufferedReader) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.字符缓冲流 字符缓冲流根据流的 ...

  8. Java基础-IO流对象之字节缓冲流(BufferedOutputStream与BufferedInputStream)

    Java基础-IO流对象之字节缓冲流(BufferedOutputStream与BufferedInputStream) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 在我们学习字 ...

  9. Java基础-IO流对象之转换流(InputStreamReader与OutoutStreamWriter)

    Java基础-IO流对象之转换流(InputStreamReader与OutoutStreamWriter) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.转换流概述 我们之前 ...

随机推荐

  1. Java面向对象程序设计

    北京电子科技学院(BESTI)                                                                                 实    ...

  2. 第一节 Linux系统简介

    一.Linux定义 Linux 是一个操作系统,就像你多少已经了解的 Windows(xp,7,8)和 Max OS. 操作系统在整个计算机系统中的角色: Linux 是系统调用和内核那两层,直观的来 ...

  3. fragment的学习

    这个讲的不错 http://blog.csdn.net/lmj623565791/article/details/37992017  Fragment与Activity交互的几种方式(二,使用Bund ...

  4. whu 1538 - B - Stones II 01背包

    题目链接: http://acm.whu.edu.cn/land/problem/detail?problem_id=1538 Problem 1538 - B - Stones II Time Li ...

  5. [二叉树建树]1119. Pre- and Post-order Traversals (30) (前序和后序遍历建立二叉树)

    1119. Pre- and Post-order Traversals (30) Suppose that all the keys in a binary tree are distinct po ...

  6. .Net用字符串拼接实现表格数据相同时合并单元格

    前言 最近在做项目通过GridView或Repeater绑定数据,如果两行或若干行某列值相同,需要进行合并单元格,但是实现过程中想到了字符串拼接,于是就没用绑定数据控件,而是用了html结合字符串实现 ...

  7. 软工网络15团队作业8——Beta阶段冲刺合集

    博客链接集合 Beta阶段敏捷冲刺计划博客 Beta阶段冲刺第一天 Beta阶段冲刺第二天 Beta阶段冲刺第三天 Beta阶段冲刺第四天 Beta阶段冲刺第五天

  8. python 创建目录

    Python对文件的操作还算是方便的,只需要包含os模块进来,使用相关函数即可实现目录的创建. 主要涉及到三个函数 1.os.path.exists(path) 判断一个目录是否存在 2.os.mak ...

  9. TP5 助手函数与TP3.2单字母函数

    一.TP5 助手函数 助手函数 描述 abort 中断执行并发送HTTP状态码 action 调用控制器类的操作 cache 缓存管理 config 获取和设置配置参数 controller 实例化控 ...

  10. windows多线程(四) 关键段 CriticalSection

    一.问题回顾 我们上一篇文章最后的程序的输出 g_Count 的值不是每次都正确,原因是没有对全局资源 g_Count 进行互斥访问(就是同一时刻只能由一个线程访问),接下来我们就来说一下使用关键段来 ...