知识点
概述
什么是序列化?
序列化就是把对象转化为字节序列的过程就是对象的序列化,反之则是对象的反序列化。
java的序列化机制是serialize接口
缺点在于:
1)序列化数据结果比较大,传输效率低
2)无法跨语言
后续xml编码格式的对象序列化机制成为了主流:
1)多语言
2)便于理解
使用恰当的序列化洗衣额不仅可以提高系统的通用性,强壮性,安全性,优化性能,同时还能让系统更加便于调试和扩展。
java序列化
java序列化实例
首先定义一个对象并且集成serialize接口
package com.hikvision.rabbitmq.serialize;
import java.io.Serializable;
/**
* @author lulinfeng
* @ClassName Person
* @Description TODO
* @autuor lulinfeng
* @Date 2020/8/13
* @Version 1.0
*/
public class Person implements Serializable {
private static final long serialVersionUID = -4035677026738967782L;
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
实现序列化及反序列化操作,采取文件流
package com.hikvision.rabbitmq.serialize;
import java.io.*;
/**
* @ClassName SerializeDemo
* @Description TODO
* @Autuor lulinfeng
* @Date 2020/8/13
* @Version 1.0
*/
public class SerializeDemo {
public static void main(String[] args) {
//序列化人员
serializePerson();
//反序列化人员
unSerializePerson();
}
private static void unSerializePerson() {
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream(new File("person")));
Person person = null;
try {
person = (Person) ois.readObject();
System.out.println("name:" + person.getName());
System.out.println("age:" + person.getAge());
System.out.println("反序列化成功");
ois.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void serializePerson() {
try {
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(new File("person")));
Person person = new Person();
person.setName("llf");
person.setAge(18);
os.writeObject(person);
System.out.println("序列化成功");
os.close();
} catch (Exception e) {
}
}
}
seriaVersionUID的作用
- 保证序列化对象和反序列化对象是同一个。序列化会有版本号,如果没有这个的话,会生成新的。
2)静态变量的序列化
序列化不保存静态变量状态
序列化之后然后修改一个静态变量的值,然后再反序列化后发现对象的值已经变成修改后的值了。
Tranisent 变量的作用
Tranisent修饰的属性不参与序列化
序列化的父子类问题
如果父类没有实现序列化,而子类实现序列化,那么父类中的成员没法做序列化操作
序列化的存储规则
对同一个对象进行多次写入,打印出第一次存储结果和第二次存储结果只多5个字节的引用关系,不会导致文件累加。
深克隆和浅克隆
1)浅克隆 :复制对象,不复制对象的引用
2)深克隆 :复制对象,复制对象的引用
可以采取序列化来实现深克隆
package com.llf.sericalizeClone;
import java.io.Serializable;
public class Teacher implements Serializable{
/**
*
*/
private static final long serialVersionUID = -6957411933291636976L;
private String Name;
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
@Override
public String toString() {
return "Teacher [Name=" + Name + "]";
}
}
创建student类来继承teacher类
package com.llf.sericalizeClone;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class Student implements Serializable{
/**
*
*/
private static final long serialVersionUID = -6029082021501636342L;
private String Name;
private int age;
private Teacher teacher;
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
public Object deapClone(){
ByteArrayOutputStream os=new ByteArrayOutputStream();
ObjectOutputStream oos=null;
ByteArrayInputStream is=null;
ObjectInputStream ois = null;
try {
//序列化
oos=new ObjectOutputStream(os);
oos.writeObject(this);
//反序列化
is=new ByteArrayInputStream(os.toByteArray());
ois=new ObjectInputStream(is);
try {
return ois.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
ois.close();
is.close();
oos.close();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
@Override
public String toString() {
return "Student [Name=" + Name + ", age=" + age + "]";
}
}
测试一下
package com.llf.sericalizeClone;
public class CloneDemo {
public static void main(String[] args) {
Teacher teacher=new Teacher();
teacher.setName("LLF");
Student student=new Student();
student.setName("CQ");
student.setAge(18);
student.setTeacher(teacher);
Student student2=(Student) student.deapClone();//克隆一个对象
System.out.println(student);
System.out.println(student2);
}
}
这边总结一下:
1)在java中,只要一个类实现了java.io.serializable接口,那么它就可以被序列化
2) 通过objectoutputstream和objectinputstream对对象进行序列化和反序列化
3)对象是否被反序列化,不仅取决于对象的代码是否一致,同样还有一个重要因素(UID)
4)序列化不保存静态变量
5)要想父类对象也参与序列化操作,必须要让父类也实现serializable接口
6)Transient主要是控制控制变量是否能被序列化。如果没有被序列化的成员变量反序列化后会被设置为初始值。
7)通过序列化操作可以实现实现深度克隆。
当前主流的序列化技术有哪些
JSON
Hessian(2)
xml
protobuf
kryo
msgpack
FST
thrify
protostuff
avro
演示几种序列化手段的demo
首先是JSON
为了方便演示
定义公用的init方法
//初始化对象
private static Person init() {
Person person = new Person();
person.setName("张三");
person.setAge(18);
return person;
}
JSON
首先引入pom
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
首先是jackson
private static void executeWithJackson() {
Person person = init();
ObjectMapper mapper = new ObjectMapper();
byte[] writeBytes = null;
long start = System.currentTimeMillis();
for (int i = 0; i < 100; i++) {
try {
//序列化
writeBytes = mapper.writeValueAsBytes(person);
} catch (JsonGenerationException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("JSON 序列化 " + (System.currentTimeMillis() - start) + "ms : 总大小为-->" + writeBytes.length);
//反序列化
try {
Person person2 = mapper.readValue(writeBytes, Person.class);
} catch (JsonParseException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
然后是fastjson
private static void executeWithFastJson() {
Person person = init();
String text = null;
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
//序列化
text = JSON.toJSONString(person);
}
System.out.println("fastjson JSON 序列化 " + (System.currentTimeMillis() - start) +
"ms : 总大小为-->" + text.getBytes().length);
//反序列化
Person person2 = JSON.parseObject(text, Person.class);
}
protobuf
首先引入pom
<dependency>
<groupId>com.baidu</groupId>
<artifactId>jprotobuf</artifactId>
<version>2.1.2</version>
</dependency>
然后使用protobuf需要在对象字段上加注解如下
然后
private static void executeWithProtoBuf() {
Person person = init();
byte[] Bytes = null;
Codec<Person> personCodec = ProtobufProxy.create(Person.class, false);
long start = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
//序列化
try {
Bytes = personCodec.encode(person);
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("protobuf JSON 序列化 " + (System.currentTimeMillis() - start) +
"ms : 总大小为-->" + Bytes.length);
//反序列化
try {
Person person2 = personCodec.decode(Bytes);
} catch (IOException e) {
e.printStackTrace();
}
}
protobuf优势就是字节数少,非常适合网络传输。
hessian
引入pom
<dependency>
<groupId>com.caucho</groupId>
<artifactId>hessian</artifactId>
<version>4.0.63</version>
</dependency>
定义方法
private static void executeWithHessian() {
Person person = init();
byte[] Bytes = null;
ByteArrayOutputStream os = new ByteArrayOutputStream();
HessianOutput ho = new HessianOutput(os);
long start = System.currentTimeMillis();
for (int i = 0; i < 100; i++) {
//序列化
try {
ho.writeObject(person);
if (i == 0) {
System.out.println(os.toByteArray().length);
}
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("hessian JSON 序列化 " + (System.currentTimeMillis() - start) +
"ms : 总大小为-->" + Bytes.length);
//反序列化
HessianInput hi = new HessianInput(new ByteArrayInputStream(os.toByteArray()));
try {
Person person2= (Person) hi.readObject();
} catch (IOException e) {
e.printStackTrace();
}
}
字节比较多,但是耗时很低
我们看下各个序列化手段的耗时和字节对比