分散式通訊之序列化

Allen烽發表於2019-02-23

知識點:
1)序列化概念
2)實現序列化手段
3)序列化實現深克隆
4)主流序列化手段及操作

Java的序列化機制 Sericalize介面 存在的問題
1)序列化資料結果比較大 傳輸效率低
2)不能跨語言對接

後續xml編碼格式的物件序列化機制成為了主流。
1)多語言
2)便於理解

Json
HTTP RESTful

基於二進位制序列化框架 MessagePack

使用恰當的序列化協議不僅可以提高系統的通用性,強壯性,安全性,優化效能,同時能讓系統更加易於除錯和擴充套件。

把物件轉化為位元組序列的過程稱為物件的序列化,反之就是物件的反序列化。

1)實現一個序列化操作
首先定義一個物件的類【實現序列化操作需要繼承sericalize介面】

package com.llf.sericalizeTest;

import java.io.Serializable;

public class Person implements Serializable{

	private static final long serialVersionUID = -8102469415223443135L;
	
	private String Name;
	private int age;
	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;
	}
}
複製程式碼

寫一個測試類,首先我們提供序列化它的方法

分散式通訊之序列化

然後執行能在相應的地方發現這個person檔案生成

分散式通訊之序列化

然後我們增加其反序列化的操作,並且把檔案中反序列化物件中的我們賦值的拿出來

package com.llf.sericalizeTest;

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;

public class SericalizeDemo {
	public static void main(String[] args) {
		//序列化操作
		SericalizePerson();
		//反序列化操作
		DeSericalizePerson();
	}

	/**
	 * 序列化Person類
	 */
	private static void SericalizePerson(){
		try {
			//生成序列化二進位制檔案的地址
			ObjectOutputStream os=new ObjectOutputStream(new FileOutputStream(new File("person")));
			Person person=new Person();
			person.setName("CQ");
			person.setAge(18);
			os.writeObject(person);
			System.out.println("序列化成功!");
			os.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private static void DeSericalizePerson(){
		try {
			ObjectInputStream ois=new ObjectInputStream(new FileInputStream(new File("person")));
			try {
				Person person=(Person) ois.readObject();
				System.out.println("名字叫 "+person.getName());
				System.out.println("年齡是 "+person.getAge());
				System.out.println("反序列化成功!");
				ois.close();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
}
複製程式碼
分散式通訊之序列化

ObjectInputStream :讀取指定的位元組資料轉化為物件

分散式通訊之序列化

serialVesionUID 的作用:
1)保證序列化物件和反序列化物件是同一個
序列化會有個版本號,如果沒有這個的話,會生成新的。
2)靜態變數的序列化
序列化不儲存靜態變數狀態
序列化之後然後修改一個靜態變數的值然後在反序列化發現物件的值已經變成修改後的值。

Transient 關鍵字
修飾的屬性不參與序列化

序列化的父子類問題:
如果父類沒有實現序列化,而子類實現序列化,那麼父類中的成員沒辦法做序列化操作。

序列化的儲存規則:
對同個物件進行多次寫入,列印出的第一次儲存結果和第二次儲存結果只多了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 + "]";
	}
}
複製程式碼
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
1)JSON
首先pom檔案中引入jar包

<dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-mapper-asl</artifactId>
    <version>1.9.13</version>
</dependency>
複製程式碼

然後程式碼

package com.llf.serialiableJSON;

import java.io.IOException;

import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;

import com.llf.sericalizeTest.Person;

public class JsonDemo {
	
	//初始化物件
	private static Person init(){
		Person person=new Person();
		person.setName("張三");
		person.setAge(18);
		return person;
	}
	
	public static void main(String[] args) {
		executeWithJackson();
	}

	@SuppressWarnings("unused")
	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();
		}
	}
}
複製程式碼
分散式通訊之序列化

同樣的阿里也是實現了一個jar包 fastjar

分散式通訊之序列化

我們比較下倆者的耗時
遍歷10萬次以下是jackson快而10萬以上是fastjson快。

package com.llf.serialiableJSON;

import java.io.IOException;

import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;

import com.alibaba.fastjson.JSON;
import com.llf.sericalizeTest.Person;

public class JsonDemo {
	
	//初始化物件
	private static Person init(){
		Person person=new Person();
		person.setName("張三");
		person.setAge(18);
		return person;
	}
	
	public static void main(String[] args) {
		executeWithJackson();
		executeWithFastJson();
	}

	@SuppressWarnings("unused")
	private static void executeWithJackson(){
		Person person=init();
		ObjectMapper mapper=new ObjectMapper();
		byte[] writeBytes=null;
		long start=System.currentTimeMillis();
		for(int i=0;i<100000;i++){
			try {
				//序列化
				writeBytes=mapper.writeValueAsBytes(person);
			} catch (JsonGenerationException e) {
				e.printStackTrace();
			} catch (JsonMappingException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		System.out.println("jackson 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();
		}
	}
	
	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);
	}
}
複製程式碼

首先引入jar包,這是百度開發的一個jar包

<dependency>
            <groupId>com.baidu</groupId>
            <artifactId>jprotobuf</artifactId>
            <version>2.1.2</version>
        </dependency>
複製程式碼

我們先定義註解

分散式通訊之序列化

然後序列化和反序列化

分散式通訊之序列化
分散式通訊之序列化

protobuf優勢就是位元組數少,非常適合網路傳輸。

hessian 序列化
定義方法

分散式通訊之序列化

位元組比較多,但是耗時很低

分散式通訊之序列化

我們看下各個序列化手段的耗時和位元組對比

分散式通訊之序列化
package com.llf.sericalizeTest;

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;

public class SericalizeDemo {
	public static void main(String[] args) {
		//序列化操作
		SericalizePerson();
		//反序列化操作
		DeSericalizePerson();
	}

	/**
	 * 序列化Person類
	 */
	private static void SericalizePerson(){
		try {
			//生成序列化二進位制檔案的地址
			ObjectOutputStream os=new ObjectOutputStream(new FileOutputStream(new File("person")));
			Person person=new Person();
			person.setName("CQ");
			person.setAge(18);
			os.writeObject(person);
			System.out.println("序列化成功!");
			os.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private static void DeSericalizePerson(){
		try {
			ObjectInputStream ois=new ObjectInputStream(new FileInputStream(new File("person")));
			try {
				Person person=(Person) ois.readObject();
				System.out.println("名字叫 "+person.getName());
				System.out.println("年齡是 "+person.getAge());
				System.out.println("反序列化成功!");
				ois.close();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}

}
複製程式碼

相關文章