Java實現(xiàn)幾種序列化方式總結
0、前言
本文主要對幾種常見Java序列化方式進行實現(xiàn)。包括Java原生以流的方法進行的序列化、Json序列化、FastJson序列化、Protobuff序列化。
1、Java原生序列化
Java原生序列化方法即通過Java原生流(InputStream和OutputStream之間的轉化)的方式進行轉化。需要注意的是JavaBean實體類必須實現(xiàn)Serializable接口,否則無法序列化。Java原生序列化代碼示例如下所示:
運行結果:
java serialize: 8ms; 總大小:420
java deserialize: 1ms; User: User [userId=null, userName=張三, passWord=123456, userInfo=張三是一個很牛的人, friends=[User [userId=null, userName=李四, passWord=123456, userInfo=李四是一個很牛的人, friends=null], User [userId=null, userName=王五, passWord=123456, userInfo=王五是一個很牛的人, friends=null]]]
2、Json序列化
Json序列化一般會使用jackson包,通過ObjectMapper類來進行一些操作,比如將對象轉化為byte數(shù)組或者將json串轉化為對象?,F(xiàn)在的大多數(shù)公司都將json作為服務器端返回的數(shù)據(jù)格式。比如調用一個服務器接口,通常的請求為xxx.json?a=xxx&b=xxx的形式。Json序列化示例代碼如下所示:
package serialize;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
*
* @author liqqc
*
*/
public class JsonSerialize {
public static void main(String[] args) throws IOException {
new JsonSerialize().start();
}
public void start() throws IOException {
User u = new User();
List<User> friends = new ArrayList<>();
u.setUserName("張三");
u.setPassWord("123456");
u.setUserInfo("張三是一個很牛的人");
u.setFriends(friends);
User f1 = new User();
f1.setUserName("李四");
f1.setPassWord("123456");
f1.setUserInfo("李四是一個很牛的人");
User f2 = new User();
f2.setUserName("王五");
f2.setPassWord("123456");
f2.setUserInfo("王五是一個很牛的人");
friends.add(f1);
friends.add(f2);
ObjectMapper mapper = new ObjectMapper();
Long t1 = System.currentTimeMillis();
byte[] writeValueAsBytes = null;
for (int i = 0; i < 10; i++) {
writeValueAsBytes = mapper.writeValueAsBytes(u);
}
System.out.println("json serialize: " + (System.currentTimeMillis() - t1) + "ms; 總大?。? + writeValueAsBytes.length);
Long t2 = System.currentTimeMillis();
User user = mapper.readValue(writeValueAsBytes, User.class);
System.out.println("json deserialize: " + (System.currentTimeMillis() - t2) + "ms; User: " + user);
}
}
運行結果:
json serialize: 55ms; 總大小:341
json deserialize: 35ms; User: User [userId=null, userName=張三, passWord=123456, userInfo=張三是一個很牛的人, friends=[User [userId=null, userName=李四, passWord=123456, userInfo=李四是一個很牛的人, friends=null], User [userId=null, userName=王五, passWord=123456, userInfo=王五是一個很牛的人, friends=null]]]
3、FastJson序列化
fastjson 是由阿里巴巴開發(fā)的一個性能很好的Java 語言實現(xiàn)的 Json解析器和生成器。特點:速度快,測試表明fastjson具有極快的性能,超越任其他的Java json parser。功能強大,完全支持java bean、集合、Map、日期、Enum,支持范型和自省。無依賴,能夠直接運行在Java SE 5.0以上版本
支持Android。使用時候需引入FastJson第三方jar包。FastJson序列化代碼示例如下所示:
package serialize;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.fastjson.JSON;
/**
*
* @author liqqc
*
*/
public class FastJsonSerialize {
public static void main(String[] args) {
new FastJsonSerialize().start();
}
public void start(){
User u = new User();
List<User> friends = new ArrayList<>();
u.setUserName("張三");
u.setPassWord("123456");
u.setUserInfo("張三是一個很牛的人");
u.setFriends(friends);
User f1 = new User();
f1.setUserName("李四");
f1.setPassWord("123456");
f1.setUserInfo("李四是一個很牛的人");
User f2 = new User();
f2.setUserName("王五");
f2.setPassWord("123456");
f2.setUserInfo("王五是一個很牛的人");
friends.add(f1);
friends.add(f2);
//序列化
Long t1 = System.currentTimeMillis();
String text = null;
for(int i = 0; i<10; i++) {
text = JSON.toJSONString(u);
}
System.out.println("fastJson serialize: " +(System.currentTimeMillis() - t1) + "ms; 總大?。? + text.getBytes().length);
//反序列化
Long t2 = System.currentTimeMillis();
User user = JSON.parseObject(text, User.class);
System.out.println("fastJson serialize: " + (System.currentTimeMillis() -t2) + "ms; User: " + user);
}
}
運行結果:
fastJson serialize: 284ms; 總大小:269
fastJson serialize: 26ms; User: User [userId=null, userName=張三, passWord=123456, userInfo=張三是一個很牛的人, friends=[User [userId=null, userName=李四, passWord=123456, userInfo=李四是一個很牛的人, friends=null], User [userId=null, userName=王五, passWord=123456, userInfo=王五是一個很牛的人, friends=null]]]
4、ProtoBuff序列化
ProtocolBuffer是一種輕便高效的結構化數(shù)據(jù)存儲格式,可以用于結構化數(shù)據(jù)序列化。適合做數(shù)據(jù)存儲或 RPC 數(shù)據(jù)交換格式。可用于通訊協(xié)議、數(shù)據(jù)存儲等領域的語言無關、平臺無關、可擴展的序列化結構數(shù)據(jù)格式。
優(yōu)點:跨語言;序列化后數(shù)據(jù)占用空間比JSON小,JSON有一定的格式,在數(shù)據(jù)量上還有可以壓縮的空間。
缺點:它以二進制的方式存儲,無法直接讀取編輯,除非你有 .proto 定義,否則無法直接讀出 Protobuffer的任何內(nèi)容。
其與thrift的對比:兩者語法類似,都支持版本向后兼容和向前兼容,thrift側重點是構建跨語言的可伸縮的服務,支持的語言多,同時提供了全套RPC解決方案,可以很方便的直接構建服務,不需要做太多其他的工作。 Protobuffer主要是一種序列化機制,在數(shù)據(jù)序列化上進行性能比較,Protobuffer相對較好。
ProtoBuff序列化對象可以很大程度上將其壓縮,可以大大減少數(shù)據(jù)傳輸大小,提高系統(tǒng)性能。對于大量數(shù)據(jù)的緩存,也可以提高緩存中數(shù)據(jù)存儲量。原始的ProtoBuff需要自己寫.proto文件,通過編譯器將其轉換為java文件,顯得比較繁瑣。百度研發(fā)的jprotobuf框架將Google原始的protobuf進行了封裝,對其進行簡化,僅提供序列化和反序列化方法。其實用上也比較簡潔,通過對JavaBean中的字段進行注解就行,不需要撰寫.proto文件和實用編譯器將其生成.java文件,百度的jprotobuf都替我們做了這些事情了。
一個帶有jprotobuf注解的JavaBean如下所示,如果你想深入學習可以參照https://github.com/google/protobuf。
package serialize;
import java.io.Serializable;
import java.util.List;
import com.baidu.bjf.remoting.protobuf.FieldType;
import com.baidu.bjf.remoting.protobuf.annotation.Protobuf;
public class User implements Serializable {
private static final long serialVersionUID = -7890663945232864573L;
@Protobuf(fieldType = FieldType.INT32, required = false, order = 1)
private Integer userId;
@Protobuf(fieldType = FieldType.STRING, required = false, order = 2)
private String userName;
@Protobuf(fieldType = FieldType.STRING, required = false, order = 3)
private String passWord;
@Protobuf(fieldType = FieldType.STRING, required = false, order = 4)
private String userInfo;
@Protobuf(fieldType = FieldType.OBJECT, required = false, order = 5)
private List<User> friends;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassWord() {
return passWord;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
}
public String getUserInfo() {
return userInfo;
}
public void setUserInfo(String userInfo) {
this.userInfo = userInfo;
}
public List<User> getFriends() {
return friends;
}
public void setFriends(List<User> friends) {
this.friends = friends;
}
@Override
public String toString() {
return "User [userId=" + userId + ", userName=" + userName + ", passWord=" + passWord + ", userInfo=" + userInfo
+ ", friends=" + friends + "]";
}
}
jprotobuf序列化代碼示例如下所示:
package serialize;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import com.baidu.bjf.remoting.protobuf.Codec;
import com.baidu.bjf.remoting.protobuf.ProtobufProxy;
/**
*
* @author liqqc
*
*/
public class ProtoBuffSerialize {
public static void main(String[] args) throws IOException {
new ProtoBuffSerialize().start();
}
public void start() throws IOException {
Codec<User> studentClassCodec = ProtobufProxy.create(User.class, false);
User u2 = new User();
List<User> friends = new ArrayList<>();
u2.setUserName("張三");
u2.setPassWord("123456");
u2.setUserInfo("張三是一個很牛的人");
u2.setFriends(friends);
User f1 = new User();
f1.setUserName("李四");
f1.setPassWord("123456");
f1.setUserInfo("李四是一個很牛的人");
User f2 = new User();
f2.setUserName("王五");
f2.setPassWord("123456");
f2.setUserInfo("王五是一個很牛的人");
friends.add(f1);
friends.add(f2);
Long stime_jpb_encode = System.currentTimeMillis();
byte[] bytes = null;
for(int i = 0; i<10; i++) {
bytes = studentClassCodec.encode(u2);
}
System.out.println("jprotobuf序列化耗時:" + (System.currentTimeMillis() - stime_jpb_encode) + "ms; 總大?。? + bytes.length);
Long stime_jpb_decode = System.currentTimeMillis();
User user = studentClassCodec.decode(bytes);
Long etime_jpb_decode = System.currentTimeMillis();
System.out.println("jprotobuf反序列化耗時:"+ (etime_jpb_decode-stime_jpb_decode) + "ms; User: " + user);
}
}
運行結果:
jprotobuf序列化耗時:9ms; 總大?。?48
jprotobuf反序列化耗時:0ms; User: User [userId=null, userName=張三, passWord=123456, userInfo=張三是一個很牛的人, friends=[User [userId=null, userName=李四, passWord=123456, userInfo=李四是一個很牛的人, friends=null], User [userId=null, userName=王五, passWord=123456, userInfo=王五是一個很牛的人, friends=null]]]
5、總結
我們通過Main方法來進行對比測試,(但是通過測試發(fā)現(xiàn)少量數(shù)據(jù)無法準確顯示每種序列化方式的優(yōu)劣,故這里無法給出比較好的答案,僅供參考)。示例代碼如下所示:
package serialize;
import java.io.IOException;
/**
* @author liqqc
*/
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ProtoBuffSerialize protoBuffSerialize = new ProtoBuffSerialize();
protoBuffSerialize.start();
System.err.println();
System.err.println();
JavaSerialize javaSerialize = new JavaSerialize();
javaSerialize.start();
System.err.println();
JsonSerialize jsonSerialize = new JsonSerialize();
jsonSerialize.start();
System.err.println();
FastJsonSerialize fastJsonSerialize = new FastJsonSerialize();
fastJsonSerialize.start();
}
}
運行結果:
jprotobuf序列化耗時:7ms; 總大?。?48
jprotobuf反序列化耗時:0ms
java serialize: 6ms; 總大?。?20
java deserialize: 1ms
json serialize: 37ms; 總大?。?41
json deserialize: 27ms
fastJson serialize: 173ms; 總大?。?69
fastJson serialize: 35ms
上面的測試僅供參考,并不能代表通過大量數(shù)據(jù)進行測試的結果。可以發(fā)現(xiàn):序列化后對象的所占大小上:protobuff序列化所占總大小是最少的;其次是fastJson序列化;最后是json序列化和java原生序列化。對于序列化耗時,上面的測試不準。
還是去看看專業(yè)測試分析吧,具體情況可以進去看看https://github.com/eishay/jvm-serializers/wiki
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
Java?通過手寫分布式雪花SnowFlake生成ID方法詳解
SnowFlake是twitter公司內(nèi)部分布式項目采用的ID生成算法,開源后廣受國內(nèi)大廠的好評。由這種算法生成的ID,我們就叫做SnowFlakeID,下面我們來詳細看看2022-04-04

