這篇文章主要講解了“何為序列化”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“何為序列化”吧!
我們提供的服務(wù)有:成都網(wǎng)站建設(shè)、網(wǎng)站設(shè)計、微信公眾號開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、木蘭ssl等。為上千余家企事業(yè)單位解決了網(wǎng)站和推廣的問題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的木蘭網(wǎng)站制作公司
序列化就是一種處理對象流
的機(jī)制。
即將對象的內(nèi)容流化,將數(shù)據(jù)轉(zhuǎn)化成字節(jié)流
,以便存儲在文件
中或用于在網(wǎng)絡(luò)
中傳輸,當(dāng)然用的最多的肯定就是網(wǎng)絡(luò)傳輸
。
從文件
中或網(wǎng)絡(luò)
上獲得序列化后的對象字節(jié)流后,根據(jù)字節(jié)流中所保存的對象狀態(tài)及描述信息,通過反序列化重建對象
。
大家所常見的序列化/反序列化的方式:Serializable
。
誰敢說沒見過Serializable,給我拖出的吊樹上打
。
JDK提供的Serializable速度較慢,原因比如:因為加入了序列化版本號,類名等信息,所以導(dǎo)致碼流變大,速度變慢等等。
所以我們要來學(xué)一些速度快的序列化方式。不僅速度快,占用的空間還小。
這么好的技術(shù),那個妹子沒學(xué)到,真是可惜了。
通用性:是否只能用于java間序列化/反序列化,是否跨語言、跨平臺
性能:分為空間開銷和時間開銷,序列化后的數(shù)據(jù)一般用于存儲或網(wǎng)絡(luò)傳輸,其大小是很重要的一個參數(shù);
當(dāng)然解析的時間也影響了序列化協(xié)議的選擇
易用性:API使用是否復(fù)雜,是否影響開發(fā)效率
可擴(kuò)展性:實體類的屬性變更會不會導(dǎo)致反序列化異常,這通常會在系統(tǒng)升級時會產(chǎn)生,參考性不是很大
Kryo 是一個快速序列化/反序列化工具,其使用了字節(jié)碼生成機(jī)制(底層依賴了 ASM 庫),因此具有比較好的運行速度。
Kryo 序列化出來的結(jié)果,是其自定義的、獨有的一種格式,不再是 JSON 或者其他現(xiàn)有的通用格式;
而且,其序列化出來的結(jié)果是二進(jìn)制的(即 byte[];而 JSON 本質(zhì)上是字符串 String);
二進(jìn)制數(shù)據(jù)顯然體積更小,序列化、反序列化時的速度也更快。
Kryo 一般只用來進(jìn)行序列化(然后作為緩存,或者落地到存儲設(shè)備之中)、反序列化,而不用于在多個系統(tǒng)、甚至多種語言間進(jìn)行數(shù)據(jù)交換 —— 目前 kryo 也只有 java 實現(xiàn)。
像redis這樣的存儲工具,是可以安全的存儲二進(jìn)制數(shù)據(jù),所以一般項目中可使用Kryo來替代JDK序列化進(jìn)行存儲。
使用場景
:(數(shù)據(jù)交換或數(shù)據(jù)持久化)比如使用kryo把對象序列化成字節(jié)數(shù)組發(fā)送給消息隊列或者放到redis等NOSQL中等等應(yīng)用場景。
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>4.0.1</version>
</dependency>
需要注意的是,由于kryo使用了較高版本的asm,可能會與業(yè)務(wù)現(xiàn)有依賴的asm產(chǎn)生沖突,這是一個比較常見的問題。只需將依賴改成:
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo-shaded</artifactId>
<version>4.0.2</version>
</dependency>
注意
:由于kryo不是線程安全的,針對多線程情況下的使用,要對kryo進(jìn)行一個簡單的封裝設(shè)計,從而可以多線程安全的使用序列化和反序列化
/**
* 序列化工具(程序調(diào)用該接口來實現(xiàn)obj<->byte[]之間的序列化/反序列化)
*/
public interface Serializer{
/**
* 序列化
*/
public void serialize(Object t,byte[] bytes);
/**
* 序列化
*/
public void serialize(Object obj, byte[] bytes, int offset, int count);
/**
* 反序列化
*/
public <T>T deserialize(byte[] bytes);
/**
* 反序列化
*/
public <T>T deserialize(byte[] bytes, int offset, int count);
}
/**
* 基于kyro的序列化/反序列化工具
*/
public class kryoSerializer implements Serializer {
// 由于kryo不是線程安全的,所以每個線程都使用獨立的kryo
final ThreadLocal<Kryo> kryoLocal = new ThreadLocal<Kryo>() {
@Override
protected Kryo initialValue() {
Kryo kryo = new Kryo();
kryo.register(ct, new BeanSerializer<>(kryo, ct));
return kryo;
}
};
// 序列化threadlocal
final ThreadLocal<Output> outputLocal = new ThreadLocal<Output>();
// 反序列化threadlocal
final ThreadLocal<Input> inputLocal = new ThreadLocal<Input>();
private Class<?> ct = null;
public kryoSerializer(Class<?> ct) {
this.ct = ct;
}
/**
* 序列化
*/
@Override
public void serialize(Object obj, byte[] bytes) {
Kryo kryo = getKryo();
Output output = getOutput(bytes);
kryo.writeObjectOrNull(output, obj, obj.getClass());
output.flush();
}
/**
* 序列化
*/
@Override
public void serialize(Object obj, byte[] bytes, int offset, int count) {
Kryo kryo = getKryo();
Output output = getOutput(bytes, offset, count);
kryo.writeObjectOrNull(output, obj, obj.getClass());
output.flush();
}
/**
* 反序列化
*/
@SuppressWarnings("unchecked")
@Override
public <T> T deserialize(byte[] bytes, int offset, int count) {
Kryo kryo = getKryo();
Input input = getInput(bytes, offset, count);
return (T) kryo.readObjectOrNull(input, ct);
}
/**
* 反序列化
*/
@Override
public <T> T deserialize(byte[] bytes) {
return deserialize(bytes, 0, bytes.length);
}
/**
* 獲取kryo
*/
private Kryo getKryo() {
return kryoLocal.get();
}
/**
* 獲取Output并設(shè)置初始數(shù)組
*/
private Output getOutput(byte[] bytes) {
Output output = null;
if ((output = outputLocal.get()) == null) {
output = new Output();
outputLocal.set(output);
}
if (bytes != null) {
output.setBuffer(bytes);
}
return output;
}
/**
* 獲取Output
*/
private Output getOutput(byte[] bytes, int offset, int count) {
Output output = null;
if ((output = outputLocal.get()) == null) {
output = new Output();
outputLocal.set(output);
}
if (bytes != null) {
output.writeBytes(bytes, offset, count);
}
return output;
}
/**
* 獲取Input
*/
private Input getInput(byte[] bytes, int offset, int count) {
Input input = null;
if ((input = inputLocal.get()) == null) {
input = new Input();
inputLocal.set(input);
}
if (bytes != null) {
input.setBuffer(bytes, offset, count);
}
return input;
}
public Class<?> getCt() {
return ct;
}
public void setCt(Class<?> ct) {
this.ct = ct;
}
Kryo致力以簡單易用的API,序列化過程中主要核心有Kryo
、Output
、Input
。
Output和Input是Kryo的IO
,他們支持以byte array
或者stream
的形式為序列化的dest和反序列化的source。
當(dāng)使用stream形式進(jìn)行寫出寫入時,需要close這些Output和Input。
寫出時,當(dāng)OutputDe buffer是滿的時候,就會flush bytes到stream中。
寫入時,會從stream中獲取bytes到Input buffer中,當(dāng)填充滿時,進(jìn)行反序列化。
和很多其他的序列化框架一樣,Kryo為了提供性能和減小序列化結(jié)果體積,提供注冊的序列化對象類的方式。
在注冊時,會為該序列化類生成int ID,后續(xù)在序列化時使用int ID唯一標(biāo)識該類型。
注冊的方式如下:
kryo.register(SomeClass.class);
或者可以明確指定注冊類的int ID,但是該ID必須大于等于0。如果不提供,內(nèi)部將會使用int++的方式維護(hù)一個有序的int ID生成。
kryo.register(SomeClass.class, 1);
這是對循環(huán)引用的支持,可以有效防止棧內(nèi)存溢出,kryo默認(rèn)會打開這個屬性。
當(dāng)你確定不會有循環(huán)引用發(fā)生的時候,可以通過以下代碼關(guān)閉循環(huán)引用檢測,從而提高一些性能,但不是很推薦
Kryo kryo = new Kryo();
kryo.setReferences(false);
如果序列化的對象類型未知并且可能為空:
kryo.writeClassAndObject(output, object);
// ...
Object object = kryo.readClassAndObject(input);
if (object instanceof SomeClass) {
// ...
}
如果對象類型已知并且可能為空:
kryo.writeObjectOrNull(output, someObject);
// ...
SomeClass someObject = kryo.readObjectOrNull(input, SomeClass.class);
如果對象類型已知并且不可能為空:
kryo.writeObject(output, someObject);
// ...
SomeClass someObject = kryo.readObject(input, SomeClass.class);
Kryo默認(rèn)是線程不安全的,有兩種解決方式:
一個是通過Threadlocal的方式為某個線程存儲一個實例:
private static final ThreadLocal<Kryo> kryoThreadLocal = new ThreadLocal<Kryo>() {
protected Kryo initialValue() {
Kryo kryo = new Kryo();
// 這里可以增加一系列配置信息
return kryo;
}
};
另外一種是通過KryoPool的方式,該方式在性能上也好于ThreadLocal:
public KryoPool createPool() {
return new KryoPool.Builder(() -> {
Kryo kryo = new Kryo();
// 此處也可以進(jìn)行一系列配置,可通過實現(xiàn)KryoFactory接口來滿足動態(tài)注冊,抽象該類
return kryo;
}).softReferences().build();
}
boolean | Boolean | byte | Byte | char |
---|---|---|---|---|
Character | short | Short | int | Integer |
long | Long | float | Float | double |
Double | byte[] | String | BigInteger | BigDecimal |
Collection | Date | Collections.emptyList | Collections.singleton | Map |
StringBuilder | TreeMap | Collections.emptyMap | Collections.emptySet | KryoSerializable |
StringBuffer | Class | Collections.singletonList | Collections.singletonMap | Currency |
Calendar | TimeZone | Enum | EnumSet |
跨語言支持較復(fù)雜
不支持對象字段的增加/刪除/修改
如果更改了對象的字段,然后再從更改前序列化的bytes中反序列化,將會出錯。
當(dāng)然如果想得到Add、Remove等操作的支持,可以使用FieldSerializer的其他擴(kuò)展,如TaggedFieldSerializer
、VersionFieldSerializer
等等。
import java.io.Serializable;
import java.util.Map;
public class Simple implements Serializable {
private static final long serialVersionUID = -4914434736682797743L;
private String name;
private int age;
private Map<String,Integer> map;
public Simple(){
}
public Simple(String name,int age,Map<String,Integer> map){
this.name = name;
this.age = age;
this.map = map;
}
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 Map<String, Integer> getMap() {
return map;
}
public void setMap(Map<String, Integer> map) {
this.map = map;
}
}
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Map;
public class OriginalSerializable {
public static void main(String[] args) throws IOException, ClassNotFoundException {
long start = System.currentTimeMillis();
setSerializableObject();
System.out.println("java原生序列化時間:" + (System.currentTimeMillis() - start) + " ms" );
start = System.currentTimeMillis();
getSerializableObject();
System.out.println("java原生反序列化時間:" + (System.currentTimeMillis() - start) + " ms");
}
public static void setSerializableObject() throws IOException{
FileOutputStream fo = new FileOutputStream("D:/file2.bin");
ObjectOutputStream so = new ObjectOutputStream(fo);
for (int i = 0; i < 100000; i++) {
Map<String,Integer> map = new HashMap<String, Integer>(2);
map.put("zhang0", i);
map.put("zhang1", i);
so.writeObject(new Simple("zhang"+i,(i+1),map));
}
so.flush();
so.close();
}
public static void getSerializableObject(){
FileInputStream fi;
try {
fi = new FileInputStream("D:/file2.bin");
ObjectInputStream si = new ObjectInputStream(fi);
Simple simple =null;
while((simple=(Simple)si.readObject()) != null){
//System.out.println(simple.getAge() + " " + simple.getName());
}
fi.close();
si.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
//e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.objenesis.strategy.StdInstantiatorStrategy;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.KryoException;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
public class KyroSerializable {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
setSerializableObject();
System.out.println("Kryo 序列化時間:" + (System.currentTimeMillis() - start) + " ms" );
start = System.currentTimeMillis();
getSerializableObject();
System.out.println("Kryo 反序列化時間:" + (System.currentTimeMillis() - start) + " ms");
}
public static void setSerializableObject() throws FileNotFoundException{
Kryo kryo = new Kryo();
kryo.setReferences(false);
kryo.setRegistrationRequired(false);
kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());
kryo.register(Simple.class);
Output output = new Output(new FileOutputStream("D:/file1.bin"));
for (int i = 0; i < 100000; i++) {
Map<String,Integer> map = new HashMap<String, Integer>(2);
map.put("zhang0", i);
map.put("zhang1", i);
kryo.writeObject(output, new Simple("zhang"+i,(i+1),map));
}
output.flush();
output.close();
}
public static void getSerializableObject(){
Kryo kryo = new Kryo();
kryo.setReferences(false);
kryo.setRegistrationRequired(false);
kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());
Input input;
try {
input = new Input(new FileInputStream("D:/file1.bin"));
Simple simple =null;
while((simple=kryo.readObject(input, Simple.class)) != null){
//System.out.println(simple.getAge() + " " + simple.getName() + " " + simple.getMap().toString());
}
input.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch(KryoException e){
}
}
}
感謝各位的閱讀,以上就是“何為序列化”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對何為序列化這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!
本文標(biāo)題:何為序列化
轉(zhuǎn)載來源:http://bm7419.com/article24/jcisje.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供小程序開發(fā)、外貿(mào)網(wǎng)站建設(shè)、App設(shè)計、網(wǎng)站收錄、App開發(fā)、企業(yè)網(wǎng)站制作
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)