概念
序列化:可以将对象转化成一个字节序列,便于存储。
反序列化:将序列化的字节序列还原
优点:可以实现对象的
”
持久性
”
, 所谓持久性就是指对象的生命周期不取决于程序
原生序列化方式
序列化方式一: 实现
Serializable
接口
(
隐式序列化
)
通过实现
Serializable
接口,这种是隐式序列化
(
不需要手动
)
,这种是最简单的序列化方式,会自动序列
化所有非
static
和
transient
关键字修饰的成员变量
class Student implements Serializable{
private String name;
private int age;
public static int QQ = 1234;
private transient String address = "CHINA";
Student(String name, int age ){
this.name = name;
this.age = age;
}
public String toString() {
return "name: " + name + "\n" +"age: " + age + "\n" +"QQ: " + QQ + "\n" + "address: " + address;
}
public void SetAge(int age) {
this.age = age;
}
}
public class SerializableDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//创建可序列化对象 System.out.println("原来的对象:");
Student stu = new Student("Ming", 16);
System.out.println(stu); //创建序列化输出流
ByteArrayOutputStream buff = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(buff);
//将序列化对象存入缓冲区
out.writeObject(stu);
//修改相关值
Student.QQ = 6666;
// 发现打印结果QQ的值被改变
stu.SetAge(18);
//发现值没有被改变
//从缓冲区取回被序列化的对象
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buff.toByteArray()));
Student newStu = (Student) in.readObject();
System.out.println("序列化后取出的对象:");
System.out.println(newStu);
}
}
打印结果:
原来的对象: name: Ming age: 16 QQ: 1234 address: CHINA
序列化后取出的对象:
name: Ming age: 16 QQ: 6666 address: null
发现
address(
被
transient)
和
QQ(
被
static)
也没有被序列化,中途修改
QQ
的值是为了以防读者误会
QQ
被
序列化了。因为序列化可以保存对象的状态,但是
QQ
的值被改变了,说明没有被序列化。
static
成员不
属于对象实例,可能被别的对象修改没办法序列化
,
序列化是序列对象。对于
address
被反序列化后由于
没有对应的引用,所以为
null
。而且
Serializable
不会调用构造方法。
PS
:细心的可能发现序列化很诱人,可以保存对象的初始信息,在以后可以回到这个初始状态。
序列化方式二:实现Externalizable接口。(显式序列化)
Externalizable
接口继承自
Serializable,
我们在实现该接口时,必须实现
writeExternal()
和
readExternal()
方法,而且只能通过手动进行序列化,并且两个方法是自动调用的,因此,这个序列化
过程是可控的,可以自己选择哪些部分序列化
public class Blip implements Externalizable{
private int i ;
private String s;
public Blip() {}
public Blip(String x, int a) {
System.out.println("Blip (String x, int a)");
s = x; i = a;
}
public String toString() { return s+i; }
@Override
public void writeExternal(ObjectOutput out) throws IOException {
// TODO Auto-generated method stub
System.out.println("Blip.writeExternal");
out.writeObject(s);
out.writeInt(i);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
// TODO Auto-generated method stub
System.out.println("Blip.readExternal");
s = (String)in.readObject();
i = in.readInt();
}
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
System.out.println("Constructing objects");
Blip b = new Blip("A Stirng", 47);
System.out.println(b);
ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("F://Demo//file1.txt"));
System.out.println("保存对象");
o.writeObject(b);
o.close();
//获得对象
System.out.println("获取对象");
ObjectInputStream in = new ObjectInputStream(new FileInputStream("F://Demo//file1.txt"));
System.out.println("Recovering b");
b = (Blip)in.readObject();
System.out.println(b);
}
}
打印结果为:
Constructing objects Blip (String x, int a) A Stirng47 保存对象 Blip.writeExternal 获取对象 Recovering b Blip.readExternal A Stirng47
当注释掉
writeExternal
和
readExternal
方法后打印结果为
:
Constructing objects Blip (String x, int a) A Stirng47 保存对象 Blip.writeExternal 获取对象 Recovering b Blip.readExternal null0
说明:
Externalizable
类会调用
public
的构造函数先初始化对象,在调用所保存的内容将对象还原。假
如构造方法不是
public
则会出现运行时错误。
序列化方式三:实现Serializable接口+添加
writeObject()和readObject()方法。(显+隐序列化)
如果想将方式一和方式二的优点都用到的话,可以采用方式三, 先实现
Serializable
接口,并且添加
writeObject()
和
readObject()
方法。注意这里是添加,不是重写或者覆盖。但是添加的这两个方法必须
有相应的格式。
1.
方法必须要被
private
修饰
—–>
才能被调用
2.
第一行调用默认的
defaultRead/WriteObject() —–>
隐式序列化非
static
和
transient
3.
调用
read/writeObject()
将获得的值赋给相应的值
—–>
显式序列化
public class SerDemo implements Serializable{
public transient int age = 23;
public String name ;
public SerDemo(){
System.out.println("默认构造器。。。");
}
public SerDemo(String name) {
this.name = name;
}
private void writeObject(ObjectOutputStream stream) throws IOException {
stream.defaultWriteObject();
stream.writeInt(age);
}
private void readObject(ObjectInputStream stream) throws ClassNotFoundException, IOException {
stream.defaultReadObject();
age = stream.readInt();
}
public String toString() { return "年龄" + age + " " + name; }
public static void main(String[] args) throws IOException, ClassNotFoundException {
SerDemo stu = new SerDemo("Ming");
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bout);
out.writeObject(stu);
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bout.toByteArray()));
SerDemo stu1 = (SerDemo) in.readObject();
System.out.println(stu1);
}
}
打印结果为:
年龄23 Ming
注释掉
stream.writeInt(age)
和
age= stream.readInt()
后:
年龄0 Ming
方式三结合了显式和隐式序列化,
Ming
被正常序列化,由于
age
被
trancient
修饰,所以需要显式序列
化。
Json序列化
Json
序列化一般会使用
jackson
包,通过
ObjectMapper
类来进行一些操作,比如将对象转化为
byte
数组
或者将
json
串转化为对象。现在的大多数公司都将
json
作为服务器端返回的数据格式。比如调用一个服
务器接口,通常的请求为
xxx.json?a=xxx&b=xxx
的形式。