Java篇之Java序列化与反序列化

Java序列化与反序列化

熟悉PHP反序列化的朋友肯定都对序列化和反序列化不陌生,在PHP中,序列化是将对象转换为字符串,而反之,反序列化则是将字符串变回为对象;而在Java中,逻辑还是这个逻辑,只不过实现的方式有一点点小小的不同,接下来我们就来讲讲Java中如何实现序列化与反序列化的

我们先来看看序列化的用途,其实主要就是两种用途:传输和保存;当两个进程在进行远程通信时,无论是传输哪种类型的数据,都是以二进制序列的形式在网络上传送的,那么发送方就需要先将Java对象转换为字节序列,而接收方再将字节序列恢复为Java对象;而保存最常见的例子就是Web服务器中的Session对象,当有10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中

PHP中,可能相对比较简单,就是因为序列化后它生成的是字符串,我们可以直观的看到它里面的类与属性,然后反序列化也是直接将字符串变成对象,这样看着就很爽;但Java不同,序列化过后它并不是字符串,而是流的形式,如果直接用记事本打开的话会发现很多的乱码,看着就很不舒服,而反序列化也是将流转换为对象;而且在PHP中一个serialize,一个unserialize就可以实现,但在Java中会稍微复杂一点;但其实原理是差不多的,无论是PHP还是Java,都是将对象中的属性按照某种特定的格式生成一段数据流,在反序列化的时候再按照这个格式将属性拿回来,再赋值给新的对象,都是只能操作对象的属性的,接下来就来看看Java中具体的操作

WriteObject

Java中,java.io.ObjectOutputStream代表对象输出流,它里面的writeObject(Object obj)方法可对指定参数obj对象进行序列化,把得到的字节序列写到一个目标输出流中,而且只有实现了SerializableExternalizable接口的类的对象才能被序列化,Externalizable接口是继承自Serializable接口的,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以采用默认的序列化方式

所以说将对象序列化需要进行如下操作:

1.创建一个对象输出流ObjectOutputStream,它可以包装一个其他类型的目标输出流,如文件输出流、字节数组输出流等
2.通过对象输出流的writeObject()方法写对象

接下来我们来实际操作操作,先来写个类:

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
32
import java.io.Serializable;

public class student implements Serializable {

private int age;
private String name;
private String sex;
public student(){}
public student(int age,String name,String sex){
this.age=age;
this.name=name;
this.sex=sex;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
public String getSex() {
return sex;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setSex(String sex) {
this.sex = sex;
}
}

然后我们来写个主类,尝试创建一个对象并且序列化它,并将序列化后的结果输出出来:

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.io.*;
import java.lang.reflect.InvocationTargetException;
public class test {
public static void main(String[] args) throws Exception {
student obj = new student(18,"Arsene.Tang","男");
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(barr);
oo.writeObject(obj);
System.out.println(obj.getName()+"对象序列化成功!");
oo.close();
System.out.println(barr);
}
}

image.png

可以看到,它就是传说中的字节数组流,然后通过调用ObjectOutputStream中的writeObject方法实现序列化

ReadObject

既然有了序列化,那相应的肯定也就有反序列化,java.io.ObjectInputStream就代表对象输入流,它里面有个readObject()方法就可以从一个源输入流中读取字节序列,再把它反序列化成一个对象返回,所以说实现一个对象的反序列化,步骤如下:

1.创建一个对象输入流ObjectInputStream,它同样可以包装一个其它类型的输入流,比如文件输入流,字节数组输入流等

2.通过对象输入流的readObject()方法读取对象

接下来就来直接看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.io.*;
import java.lang.reflect.InvocationTargetException;
public class test {
public static void main(String[] args) throws Exception {
student obj = new student(18,"Arsene.Tang","男");
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(barr);
oo.writeObject(obj);
System.out.println(obj.getName()+"对象序列化成功!");
oo.close();
System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = (Object) ois.readObject();
student s = (student) o;
System.out.println(s.getName()+"对象反序列化成功!");
System.out.println("您的姓名为:"+s.getName()+" 您的年龄为:"+s.getAge()+" 您的性别为:"+s.getSex());
}
}

首先是创建一个对象输入流ois,里面包装了一个字节数组输入流,然后将它反序列化成一个对象o,再通过强制类型转换将它转换为student类型,接下来就可以正常使用这个student对象了,看看运行结果:

image.png

总结

总结一下就是当需要序列化时,先创建ObjectOutputStream对象,再调用里面的writeObject()方法;当需要反序列化时,则先创建ObjectInputStream对象,再调用里面的readObject()方法即可,当然需要注意的就是只有继承了Serializable接口的类才能被序列化哈,这在后面cc链的分析中还是挺重要的

参考文章:

https://www.cnblogs.com/xdp-gacl/p/3777987.html

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2023 Arsene.Tang
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信