0x01 序列化和反序列化概念 序列化就是把对象转换成字节流,便于保存在内存、文件、数据库中;反序列化即逆过程,把字节流还原成对象。
0x02 为什么要引入序列化 究竟为什么要用序列化,不用不行吗?
先看下序列化用途:
(1)永久性保存对象,保存对象的字节序列到本地文件或者数据库中 ;
(2)通过序列化以字节流的形式使对象在网络中进行传递和接收 ;
(3)通过序列化在进程间传递对象 。
网上看到基本就是这三个,如上图,基本就是在文件、网络、进程中传输字节流。看到这里还是不知道啊,往下看,网上找的栗子看了就懂了。
第一个例子:
Web 服务器中的 Session 会话对象,当有10万用户并发访问,就有可能出现10万个 Session 对象,显然这种情况内存可能是吃不消的。于是 Web 容器就会把一些 Session 先序列化,让他们离开内存空间,序列化到硬盘中,当需要调用时,再把保存在硬盘中的对象还原到内存中。
第二个例子:
当两个进程进行远程通信时,文本、图片、音频、视频的传输可以发送各种类型的数据,包括等, 而这些数据都会以二进制序列的形式在网络上传送。
到这里基本了解了序列化和反序列化,那具体java是怎么实现序列化的。
0x03 Java序列化 在Java中,主要有两个接口ObjectOutputStream(对象输出流) 和ObjectInputStream(对象输入流),ObjectOutputStream.writeObject()实现序列化,而ObjectInputStream.readObject()方法实现反序列化。
序列化 反序列化例子1: 执行命令的类CommandExec.java:
CommandExec.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import java.io.*;public class CommandExec implements Serializable { public CommandExec () { String cmd = "calc" ; try { Process process = Runtime.getRuntime().exec(cmd); InputStream is = process.getInputStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); for (String content = br.readLine(); content != null ; content = br.readLine()) { System.out.println(content); } } catch (IOException var7) { var7.printStackTrace(); } } }
main.java
main.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import java.io.*;public class main { public static void main (String[] args) throws Exception { serializeCommandExec(); CommandExec commandExec = (CommandExec) deserializeCommandExec(); } private static void serializeCommandExec () throws IOException { CommandExec commandExec = new CommandExec(); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("d:/test.txt" ))); oos.writeObject(commandExec); System.out.println("对象序列化成功!" ); } private static CommandExec deserializeCommandExec () throws Exception { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("d:/test.txt" ))); CommandExec commandExec = (CommandExec) ois.readObject(); System.out.println("对象反序列化成功!" ); return commandExec; } }
以上代码的运行结果如下图,在构造对象时弹出1个计算器,序列化生成test.txt文件,并且由文件反序列化成一个对象:
上面代码CommandExec.java中在构造函数中我写的是执行calc计算器,这里本来我以为反序列化也可以弹出计算器,但是我的想法是错误的,查了下发现反序列不会调用构造方法,但是会调用父类构造方法,所以我这里改下CommandExec.java,让它继承一个父类,在父类中执行计算器。
父类Command.java:
Command.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;public class Command { public Command () { String cmd = "calc" ; System.out.println("2222" ); try { Process process = Runtime.getRuntime().exec(cmd); InputStream is = process.getInputStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); for (String content = br.readLine(); content != null ; content = br.readLine()) { System.out.println(content); } } catch (IOException var7) { var7.printStackTrace(); } } }
CommandExec.java
CommandExec.java 1 2 3 4 5 6 7 8 import java.io.*;public class CommandExec extends Command implements Serializable { public CommandExec () { System.out.println("1111" ); } }
运行结果可以发现是对的,反序列化的时候确实执行了父类的构造方法 没有调用子类的构造方法。序列化之前构造对象时弹出了2个计算器,因为分别调用了父类和子类的构造方法,而反序列化之后生成对象也弹出了1个计算器,所以总共弹出3个计算器窗口。
反序列化例子2: 后续学习遇到的困惑,这里更新下。
该例子重写了readObject()方法,当我们调用反序列化时会调用ReadObjectOverride.readObject()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import java.io.*;public class ReadObjectTest { public static void main (String[] args) throws Exception { ReadObjectOverride readObjectOverride = new ReadObjectOverride(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(readObjectOverride); oos.flush(); oos.close(); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); Object obj = (Object) ois.readObject(); } } class ReadObjectOverride implements Serializable { private void readObject (java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); Runtime.getRuntime().exec("calc" ); } }
运行结果:
参考链接
https://baijiahao.baidu.com/s?id=1636492159314232573&wfr=spider&for=pc
https://www.cnblogs.com/niceyoo/p/10596657.html
https://www.itranslater.com/qa/details/2582634340228269056
https://blog.csdn.net/weixin_29324013/article/details/80901275
https://paper.seebug.org/312/