先说一下,前端时间做项目的时候,遇到一个需求就是需要定时去扫描一张表,并且将没有扫描过的数据在另一张表中进行操作,所以需要记住上一次扫描的主键ID,并且在重启服务后能不丢失。于是想到了序列化,但是没弄好,就采用了修改properties的方式(当然会有一个单利的类,对内存中的进行操作,提供效率)。
一、Java序列化
1、Java序列化: 把对象转化为字节序列的过程称为序列化,反之称为反序列化。
2、Java序列化的缺点:1)、结果为二进制文件,文件比较大,传输过程较长
2)、不能跨语言
3、serialVersionUID的作用,摘要算法生成一个唯一编号(类似于下载文件的MD5加密的作用)。
4、序列化的演进:
1)、由于java序列化的缺点很长一段时候都是使用xml进行对象的序列化,对语言进行兼容的同时,也比二进制的序列化容易理解。
即基于xml的soap协议对应的webservice很长一段时间成为标准。
2)、json的简单文本编码格式的Http restful 接口,但是json的问题在于序列化的的空间大、性能低等。
3)、为满足移动客户端的快速响应的体验,于是二进制协议成为热点,如:messagePack。
二、序列化的特点:
1、序列化不保存静态变量
2、transient:关键字修改的字段不参与序列化,则为其类型的默认值
3、如果父类没有序列化,而子类实现序列化,则子类中的父类成员不能进行序列化
4、序列化的存储规则:对同一对象进行多次写入,打印出的存储结果和第二次的存储结果,只是多了5个字节的引用关系,并不会累加文件。
package com.kevin.demo;
import java.io.Serializable;
public class Person extends ParentPerson implements Serializable{
/**
*
*/
private static final long serialVersionUID = -1410109129055379293L;
private static String name;
private String address;
private transient int age;
@Override
public String toString() {
return "Person [name=" + name + "address=" + address + ", age=" + age + "]";
}
public static String getName() {
return name;
}
public static void setName(String name) {
Person.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
package com.kevin.demo;
public class ParentPerson {
private String oldName;
public String getOldName() {
return oldName;
}
public void setOldName(String oldName) {
this.oldName = oldName;
}
}
package com.kevin.demo;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* 序列化反序列化工具类
* (如果写工具类可以考虑写的根目录的一个特定文件夹下,包名称的子文件夹下)
* @author kevin
*
*/
public class SerializeUtil {
public static void main(String[] args) {
// 序列化对象
// serialize();
// 反序列化
unSerialize();
}
/**
* 序列化(将对象序列化到项目的根目录下)
*/
public static void serialize(){
try {
ObjectOutputStream oStream = new ObjectOutputStream(new FileOutputStream(new File("person")));
Person person = new Person();
person.setName("kevin");
person.setAge(18);
person.setOldName("old kevin");
person.setAddress("beijing");
oStream.writeObject(person);
System.out.println("序列化成功!");
oStream.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void unSerialize(){
try {
ObjectInputStream iStream = new ObjectInputStream(new FileInputStream(new File("person")));
Person person = (Person)iStream.readObject();
System.out.println("反序列化成功!");
System.out.println(person);
iStream.close();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
反序列化成功!
Person [name=nulladdress=beijing, age=0]
三、序列化实现深度克隆
1、浅拷贝(浅复制、浅克隆): 被复制对象的所以变量都有与原来的对象相同的值,而所有的其他对象的引用任然指向原来的对象。
2、深拷贝(深复制、深克隆):被复对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。即深拷贝,需要将对象引用的对象一并进行拷贝。
public class Student implements Serializable{
private static final long serialVersionUID = 5630895052908986144L;
private String name;
private int age;
private Teacher teacher;
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;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
public Object deepClone() throws IOException, ClassNotFoundException {
//序列化
ByteArrayOutputStream baos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(baos);
oos.writeObject(this);
//反序列化
ByteArrayInputStream bais=new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois=new ObjectInputStream(bais);
return ois.readObject();
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", teacher=" + teacher +
'}';
}
}
public class Teacher implements Serializable{
private static final long serialVersionUID = -6635991328204468281L;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
'}';
}
}
public class CloneDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Teacher teacher=new Teacher();
teacher.setName("mic");
Student student=new Student();
student.setName("kevin");
student.setAge(18);
student.setTeacher(teacher);
Student student2=(Student) student.deepClone(); //
System.out.println(student);
student2.getTeacher().setName("james");
System.out.println(student2);
}
}
四、主流的序列化技术及性能
JSON、 dubbo采用的 Hessian(2) 、xml、protobuf、kryo、MsgPack、FST、thrift、protostuff、Avro
用代码简单分析一下性能问题:
public class JsonDemo {
//初始化
private static Person init(){
Person person=new Person();
person.setName("mic");
person.setAge(18);
return person;
}
public static void main(String[] args) throws IOException {
// excuteWithJack();
excuteWithFastJson();
// excuteWithProtoBuf();
//
// excuteWithHessian();
}
private static void excuteWithJack() throws IOException {
Person person=init();
ObjectMapper mapper=new ObjectMapper();
byte[] writeBytes=null;
Long start=System.currentTimeMillis();
for(int i=0;i<10000;i++){
writeBytes=mapper.writeValueAsBytes(person);
}
System.out.println("Json序列化:"+(System.currentTimeMillis()-start)+"ms : " +
"总大小->"+writeBytes.length);
Person person1=mapper.readValue(writeBytes,Person.class);
System.out.println(person1);
}
private static void excuteWithFastJson() throws IOException {
Person person=init();
String text=null;
Long start=System.currentTimeMillis();
for(int i=0;i<10000;i++){
text=JSON.toJSONString(person);
}
System.out.println("fastjson序列化:"+(System.currentTimeMillis()-start)+"ms : " +
"总大小->"+text.getBytes().length);
Person person1=JSON.parseObject(text,Person.class);
System.out.println(person1);
}
private static void excuteWithProtoBuf() throws IOException {
Person person=init();
Codec<Person> personCodec= ProtobufProxy.create(Person.class,false);
Long start=System.currentTimeMillis();
byte[] bytes=null;
for(int i=0;i<10000;i++){
bytes=personCodec.encode(person);
}
System.out.println("protobuf序列化:"+(System.currentTimeMillis()-start)+"ms : " +
"总大小->"+bytes.length);
Person person1=personCodec.decode(bytes);
System.out.println(person1);
}
private static void excuteWithHessian() throws IOException {
Person person=init();
ByteArrayOutputStream os=new ByteArrayOutputStream();
HessianOutput ho=new HessianOutput(os);
Long start=System.currentTimeMillis();
for(int i=0;i<10000;i++){
ho.writeObject(person);
if(i==0){
System.out.println(os.toByteArray().length);
}
}
System.out.println("Hessian序列化:"+(System.currentTimeMillis()-start)+"ms : " +
"总大小->"+os.toByteArray().length);
HessianInput hi=new HessianInput(new ByteArrayInputStream(os.toByteArray()));
Person person1=(Person)hi.readObject();
System.out.println(person1);
}
}
依赖的jar如下:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.31</version>
</dependency>
<dependency>
<groupId>com.baidu</groupId>
<artifactId>jprotobuf</artifactId>
<version>2.1.2</version>
</dependency>
<dependency>
<groupId>com.caucho</groupId>
<artifactId>hessian</artifactId>
<version>4.0.38</version>
</dependency>
</dependencies>