Java序列化是指把Java对象转换为字节序列的过程,Java反序列化是指把字节序列恢复为Java对象的过程
主要有两种用途: 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中; 在网络上传送对象的字节序列。
不管什么用途我们都希望占用空间小,传输效率高,读写快。方式有多中,效率不同,常见的方式有jdk的ObjectOutputStream、json类库、javax的xml、googe的protobuf及基于protobuf的protostuff。
用到的实体People.java
public class People implements Serializable {
private static final long serialVersionUID = 1L;
private String id;
private String name;
private String age;
private String birthday;
public String getId() {
return id;
}
public People(String id, String name, String age, String birthday) {
super();
this.id = id;
this.name = name;
this.age = age;
this.birthday = birthday;
}
public People() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
public void setId(String id) {
this.id = id;
}
}
测试类
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
import com.ultradata.task.software.People;
import redis.clients.jedis.Jedis;
public class JDKSerializeUtil {
/**
* 序列化(对象 -> 字节数组)
* @param object
* @return
*/
private static byte[] serialize(Object object) {
ObjectOutputStream objectOutputStream = null;
ByteArrayOutputStream byteArrayOutputStream = null;
byte[] getByte = null;
try {
byteArrayOutputStream = new ByteArrayOutputStream();
objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(object);
getByte = byteArrayOutputStream.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return getByte;
}
/**
* 反序列化(字节数组 -> 对象)
* @param binaryByte
* @return
*/
private static Object deserizlize(byte[] binaryByte) {
ObjectInputStream objectInputStream = null;
ByteArrayInputStream byteArrayInputStream = null;
Object readObject = null;
try {
byteArrayInputStream = new ByteArrayInputStream(binaryByte);
objectInputStream = new ObjectInputStream(byteArrayInputStream);
readObject = objectInputStream.readObject();
} catch (Exception e) {
e.printStackTrace();
}
return readObject;
}
public static void main(String[] args) {
Jedis jedis = null;
try {
jedis = new Jedis("192.168.172.74");
People people = new People("0", "张三", "30", "2019-01-17");
jedis.set("zs".getBytes(), serialize(people));
byte[] getByte = jedis.get("zs".getBytes());
People p = (People)deserizlize(getByte);
System.out.println(p.getId()+"--"+p.getName()+"-"+p.getAge()+"-"+p.getBirthday());
System.out.println("------------------");
People p1 = new People("1", "张三", "34", "2019-01-18");
People p2 = new People("2", "李四", "28", "2019-01-19");
List list = new ArrayList<>();
list.add(p1);
list.add(p2);
jedis.set("list".getBytes(), serialize(list));
byte[] bytesList = jedis.get("list".getBytes());
@SuppressWarnings("unchecked")
List lists = (List)deserizlize(bytesList);
for (People pp : lists) {
System.out.println(pp.getId()+"--"+pp.getName()+"-"+pp.getAge()+"-"+pp.getBirthday());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (jedis != null) {
jedis.close();
}
}
}
}
redis-cli中 显示
192.168.172.74:6379> get obj
"\xac\xed\x00\x05sr\x00\"com.ultradata.task.software.People\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x04L\x00\x03aget\x00\x12Ljava/lang/String;L\x00\bbirthdayq\x00~\x00\x01L\x00\x02idq\x00~\x00\x01L\x00\x04nameq\x00~\x00\x01xpt\x00\x0230t\x00\n2019-01-17t\x00\x010t\x00\x06\xe5\xbc\xa0\xe4\xb8\x89"
192.168.172.74:6379> get list
"\xac\xed\x00\x05sr\x00\x13java.util.ArrayListx\x81\xd2\x1d\x99\xc7a\x9d\x03\x00\x01I\x00\x04sizexp\x00\x00\x00\x02w\x04\x00\x00\x00\x02sr\x00\"com.ultradata.task.software.People\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x04L\x00\x03aget\x00\x12Ljava/lang/String;L\x00\bbirthdayq\x00~\x00\x03L\x00\x02idq\x00~\x00\x03L\x00\x04nameq\x00~\x00\x03xpt\x00\x0234t\x00\n2019-01-18t\x00\x011t\x00\x06\xe5\xbc\xa0\xe4\xb8\x89sq\x00~\x00\x02t\x00\x0228t\x00\n2019-01-19t\x00\x012t\x00\x06\xe6\x9d\x8e\xe5\x9b\x9bx"
192.168.172.74:6379>
控制台输出
0--张三-30-2019-01-17
------------------
1--张三-34-2019-01-18
2--李四-28-2019-01-19
1、Protobuf介绍
Protobuf是 Google 公司内部的混合语言数据标准,为Google Protocol Buffer缩写。 目前已经正在使用的有超过 48,162 种报文格式定义和超过 12,183 个 .proto 文件。与平台、语言无关,可扩展,解析速度快效率高,占用空间小。目前提供了 C++、Java、Python ,Ruby、Go等语言的 API。
2、Protobuf使用流程
需要自己写一个.proto文件用来描述序列化的格式,然后用Protobuf提供的protoc工具将.proto文件编译成一个Java文件,最后将该Java文件引入到项目中就可以了。
(1)依赖安装
yum -y install autoconf automake libtool curl make g++ unzip
(2)下载源码
已经安装git的情况下
$ git clone https://github.com/protocolbuffers/protobuf.git
$ cd protobuf
#初始化本地子仓库,并从远程父仓库检出对应的两个子库,一个基准测试库,一个谷歌C++测试库
$ git submodule update --init --recursive #对我们使用没有protobuf影响,可以不执行
$ ./autogen.sh
没有git的直接下载protobuf的git仓库https://github.com/protocolbuffers/protobuf
$ unzip protobuf-master.zip
$ cd protobuf-master
$ ./autogen.sh
或者直接去https://github.com/protocolbuffers/protobuf/releases/latest最新源码包,可以全部下载,也可以选择单个语言下载,这里我选java,下载protobuf-java-3.6.1.tar.gz(推荐此方式,简单)
$ tar -zxvf protobuf-java-3.6.1.tar.gz
$ cd protobuf-3.6.1
(3)编译安装
$ ./configure
$ make
$ make check
$ sudo make install
$ sudo ldconfig # refresh shared library cache.
(4)编写proto文件
syntax = "proto3";
option java_package = "com.serialize.protobuf";
option java_outer_classname="PersonProtobuf";
message Person {
string id= 1;
string name = 2;
int32 age = 3;
string birthday = 4;
Address domicile = 5;
message Address{
string province = 1;
string city = 2;
string district = 3;
DomicileType type = 4;
}
enum DomicileType {
COUNTRY = 0;
CITY = 1;
NO_CITY = 2;
}
}
java_package包名,java_outer_classname类名。这里使用了proto3语法编译,移除了 “required”。关于protobuf的更多使用信息后续会进行介绍,也可以看别同学的文章。
(5)生成java代码
进入到安装路径下/usr/local/protobuf,有bin include lib三个文件夹,bin下有protoc命令
用来生成java代码
bin/protoc --proto_path=./src/proto/ --java_out=src/java/ PersonProtobuf.proto
--proto_path是我们自己编写的proto文件路劲,--java_out是我们产生java文件的路径;PersonProtobuf.proto是源文件
执行命令后会在src/java/com/serialize/protobuf下生成PersonProtobuf.java文件,com/serialize/protobuf是包名
(6)引入maven依赖
com.google.protobuf
protobuf-java
3.6.1
com.google.protobuf
protobuf-java-util
3.6.1
(7)序列化使用
把生成PersonProtobuf.java文件拷贝到项目中
测试类如下:
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import org.springframework.data.redis.PersonProtobuf.Person;
import org.springframework.data.redis.PersonProtobuf.Person.Address;
import org.springframework.data.redis.PersonProtobuf.Person.DomicileType;
import com.google.protobuf.InvalidProtocolBufferException;
import redis.clients.jedis.Jedis;
public class ProtobufSerializetionUtil {
public static void main(String[] args) {
Jedis jedis = null;
try {
jedis = new Jedis("192.168.172.74");
PersonProtobuf.Person.Builder personBuilder = PersonProtobuf.Person.newBuilder();
personBuilder.setId("123456");
personBuilder.setName("张三");
personBuilder.setAge(20);
personBuilder.setBirthday("2019-02-20");
PersonProtobuf.Person.Address.Builder place = PersonProtobuf.Person.Address.newBuilder();
place.setProvince("北京");
place.setCity("北京");
place.setDistrict("朝阳");
place.setType(DomicileType.CITY);
personBuilder.setDomicile(place);
PersonProtobuf.Person person = personBuilder.build();
//第一种方式
//序列化
byte[] bytes = person.toByteArray();//获取字节数组,适用于SOCKET或者保存在磁盘。
jedis.set("protobuf-obj".getBytes(), bytes);
//反序列化
PersonProtobuf.Person per = PersonProtobuf.Person.parseFrom(jedis.get("protobuf-obj".getBytes()));
System.out.println(per.getId()+"-"+per.getAge()+"-"+per.getName()+"-"+per.getBirthday());
Address add = per.getDomicile();
System.out.println(add.getProvince()+"-"+add.getCity()+"-"+add.getDistrict()+"-"+add.getType());
//第二种序列化:粘包,将一个或者多个protobuf对象字节写入stream。
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
//生成一个由:[字节长度][字节数据]组成的package。特别适合RPC场景
person.writeDelimitedTo(byteArrayOutputStream);
//反序列化,从steam中读取一个或者多个protobuf字节对象
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
Person personinfo = PersonProtobuf.Person.parseDelimitedFrom(byteArrayInputStream);
System.out.println(personinfo);
System.out.println("-----------------------");
//第三种序列化,写入文件或者Socket,不太常用
FileOutputStream fileOutputStream = new FileOutputStream(new File("/test.dt"));
person.writeTo(fileOutputStream);
fileOutputStream.close();
FileInputStream fileInputStream = new FileInputStream(new File("/test.dt"));
personinfo = PersonProtobuf.Person.parseFrom(fileInputStream);
System.out.println(personinfo);
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (jedis!=null) {
jedis.close();
}
}
}
}
redis-cli中显示
192.168.172.74:6379> get protobuf-obj
"\n\x06123456\x12\x06\xe5\xbc\xa0\xe4\xb8\x89\x18\x14\"\n2019-02-20*\x1a\n\x06\xe5\x8c\x97\xe4\xba\xac\x12\x06\xe5\x8c\x97\xe4\xba\xac\x1a\x06\xe6\x9c\x9d\xe9\x98\xb3 \x01"
192.168.172.74:6379>
控制台输出
123456-20-张三-2019-02-20
北京-北京-朝阳-CITY
------------------------
id: "123456"
name: "\345\274\240\344\270\211"
age: 20
birthday: "2019-02-20"
domicile {
province: "\345\214\227\344\272\254"
city: "\345\214\227\344\272\254"
district: "\346\234\235\351\230\263"
type: CITY
}
-----------------------
id: "123456"
name: "\345\274\240\344\270\211"
age: 20
birthday: "2019-02-20"
domicile {
province: "\345\214\227\344\272\254"
city: "\345\214\227\344\272\254"
district: "\346\234\235\351\230\263"
type: CITY
}
protostuff基于Google Protobuf的封装的,不用用户自己写.proto文件,使用起来比较方便,在几乎不损耗性能的情况下即可实现对象的序列化与反序列化。
在pom.xml引入maven依赖
io.protostuff
protostuff-runtime
1.6.0
io.protostuff
protostuff-core
1.6.0
test
集合包装类DataListWrapper.java
public class DataListWrapper {
private List dataList = new ArrayList<>();
public DataListWrapper() {
}
public DataListWrapper(List dataList) {
this.dataList = dataList;
}
public List getDataList() {
return dataList;
}
public void setDataList(List dataList) {
this.dataList = dataList;
}
}
序列化工具类ProtostuffSerializer.java
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;
/**
* 序列化和反序列化工具
* Protostuff
*/
public class ProtostuffSerializer {
private static Map, Schema>> cachedSchema = new ConcurrentHashMap<>();
/**
* 序列化(对象 -> 字节数组)
* @param obj
* @return
*/
@SuppressWarnings("unchecked")
public static byte[] serialize(final T obj) {
Class clazz = (Class) obj.getClass();
// Schema schema = (Schema) RuntimeSchema.createFrom(clazz);
Schema schema = (Schema) getSchema(clazz);
final LinkedBuffer linkedBuffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
try {
return ProtostuffIOUtil.toByteArray(obj, schema, linkedBuffer);
} catch (final Exception e) {
throw new IllegalStateException(e.getMessage(), e);
} finally {
linkedBuffer.clear();
}
}
/**
* 反序列化(字节数组 -> 对象)
* @param bytes
* @param clazz
*/
public static T deserialize(final byte[] bytes,Class clazz) {
try {
// Schema schema = (Schema) RuntimeSchema.getSchema(clazz);
Schema schema = getSchema(clazz);
T newMessage = schema.newMessage();
ProtostuffIOUtil.mergeFrom(bytes, newMessage, schema);;
if (newMessage != null) {
return newMessage;
}
} catch (final Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
return null;
}
/**
* 用ConcurrentHashMap缓存首次创建的Schema对象
* 避免每次重复创建
* @param cls
*/
@SuppressWarnings("unchecked")
private static Schema getSchema(Class claszz) {
Schema schema = (Schema) cachedSchema.get(claszz);
if (schema == null) {
schema = RuntimeSchema.createFrom(claszz);
cachedSchema.put(claszz, schema);
}
return schema;
}
}
测试类(redis相关类较易,就不出示了)
import java.util.ArrayList;
import java.util.List;
import org.icm.net.tcp.serializer.ProtostuffSerializer;
import redis.clients.jedis.Jedis;
public class TestSerializer{
/**
* get String
* @param key
* @return
*/
public synchronized static String getString(String key) {
Jedis jedis = null;
try {
jedis = RedisClient.getJedis();
if (jedis == null) {
return null;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
RedisClient.closeJedis(jedis);
}
return jedis.get(key);
}
public synchronized static String setString(byte[] key,byte[] value) {
Jedis jedis = null;
try {
jedis = RedisClient.getJedis();
if (jedis == null) {
return null;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
RedisClient.closeJedis(jedis);
}
return jedis.set(key,value);
}
public static void main(String[] args) {
//对象序列化,对象people序列化后存redis
People people = new People("0", "张三", "30", "2019-01-17");
setString("111".getBytes(),ProtostuffSerializer.serialize(people));
//从redis取出并反序列化
People pp = ProtostuffSerializer.deserialize(getString("111").getBytes(),People.class);
System.out.println(pp.getId()+"--"+pp.getName()+"-"+pp.getAge()+"-"+pp.getBirthday());
System.out.println("--------------------");
//集合序列化,把对象people 封装对象中的集合
People p1 = new People("1", "张三", "34", "2019-01-18");
People p2 = new People("2", "李四", "28", "2019-01-19");
List list = new ArrayList<>();
list.add(p1);
list.add(p2);
DataListWrapper dataListWrapper = new DataListWrapper(list);
//序列化后存redis
byte[] bytesValue = ProtostuffSerializer.serialize(dataListWrapper);
setString("222".getBytes(),bytesValue);
//从redis取出并反序列化
byte[] getBytes = getString("222").getBytes();
DataListWrapper wrapper = ProtostuffSerializer.deserialize(getBytes,DataListWrapper.class);
for (People p : (List)wrapper.getDataList()) {
System.out.println(p.getId()+"--"+p.getName()+"-"+p.getAge()+"-"+p.getBirthday());
}
}
}
redis-cli中显示
192.168.172.74:6379> get 111
"\n\x010\x12\x06\xe5\xbc\xa0\xe4\xb8\x89\x1a\x0230\"\n2019-01-17"
192.168.172.74:6379> get 222
"\x0b\n\x011\x12\x06\xe5\xbc\xa0\xe4\xb8\x89\x1a\x0234\"\n2019-01-18\x0c\x0b\n\x012\x12\x06\xe6\x9d\x8e\xe5\x9b\x9b\x1a\x0228\"\n2019-01-19\x0c"
192.168.172.74:6379>
控制台输出
0--张三-30-2019-01-17
---------------------
1--张三-34-2019-01-18
2--李四-28-2019-01-19
常见的json库有JSON-lib、FastJson、Jackson、Gson
在pom.xml中引入maven依赖
com.fasterxml.jackson.core
jackson-annotations
2.9.6
com.fasterxml.jackson.core
jackson-core
2.9.6
com.fasterxml.jackson.core
jackson-databind
2.9.6
jackson的序列化如下:
import java.util.ArrayList;
import java.util.List;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
import com.ultradata.task.software.People;
import redis.clients.jedis.Jedis;
public class JsonSerializeUtil {
public static void main(String[] args) {
Jedis jedis = null;
try {
jedis = new Jedis("192.168.172.74");
//对象序列化
People people = new People("0", "张三", "30", "2019-01-17");
ObjectMapper objectMapper = new ObjectMapper();
byte[] writeValueAsBytes = objectMapper.writeValueAsBytes(people);
jedis.set("jsonobj".getBytes(), writeValueAsBytes);
byte[] getByte = jedis.get("jsonobj".getBytes());
People p = objectMapper.readValue(getByte, People.class);
System.out.println(p.getId()+"--"+p.getName()+"-"+p.getAge()+"-"+p.getBirthday());
System.out.println("------------------");
//集合list序列化
People p1 = new People("1", "张三", "34", "2019-01-18");
People p2 = new People("2", "李四", "28", "2019-01-19");
List list = new ArrayList<>();
list.add(p1);
list.add(p2);
byte[] jsonbyte = objectMapper.writeValueAsBytes(list);
jedis.set("jsonlist".getBytes(), jsonbyte);
byte[] getBytess = jedis.get("jsonlist".getBytes());
//因为list没有List.class操作,TypeReference可以传递完整的泛型类型信息,并避免类型擦除的问题
TypeReference> typeReference = new TypeReference>(){};
List lists = objectMapper.readValue(getBytess,typeReference);
for (People pp : lists) {
System.out.println(pp.getId()+"--"+pp.getName()+"-"+pp.getAge()+"-"+pp.getBirthday());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (jedis != null) {
jedis.close();
}
}
}
}
redis-cli中输出
192.168.172.74:6379> get jsonobj
"{\"name\":\"\xe5\xbc\xa0\xe4\xb8\x89\",\"id\":\"0\",\"age\":\"30\",\"birthday\":\"2019-01-17\"}"
192.168.172.74:6379> get jsonlist
"[{\"name\":\"\xe5\xbc\xa0\xe4\xb8\x89\",\"id\":\"1\",\"age\":\"34\",\"birthday\":\"2019-01-18\"},{\"name\":\"\xe6\x9d\x8e\xe5\x9b\x9b\",\"id\":\"2\",\"age\":\"28\",\"birthday\":\"2019-01-19\"}]"
192.168.172.74:6379>
控制台输出
0--张三-30-2019-01-17
------------------
1--张三-34-2019-01-18
2--李四-28-2019-01-19
和之前不同的是需要在实体类上加注解
People类上@XmlRootElement(name = "People")
DataListWrapper类上@XmlRootElement(name = "DataListWrapper")
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import com.ultradata.task.software.DataListWrapper;
import com.ultradata.task.software.People;
import redis.clients.jedis.Jedis;
public class XMLSerializeUtil {
private static String serialize(Object object) {
StringWriter stringWriter = null;
try {
stringWriter = new StringWriter();
JAXBContext jContext = JAXBContext.newInstance(object.getClass());
Marshaller marshaller = jContext.createMarshaller();
marshaller.marshal(object, stringWriter);
if (stringWriter!=null) {
return stringWriter.toString();
}
} catch (JAXBException e) {
e.printStackTrace();
}
return null;
}
private static Object deserizlize(Class> clazz, String xmlString) {
Object xmlObject = null;
try {
JAXBContext context = JAXBContext.newInstance(clazz);
Unmarshaller unmarshaller = context.createUnmarshaller();
StringReader stringReader = new StringReader(xmlString);
xmlObject = unmarshaller.unmarshal(stringReader);
} catch (JAXBException e) {
e.printStackTrace();
}
return xmlObject;
}
public static void main(String[] args) {
Jedis jedis = null;
try {
jedis = new Jedis("192.168.172.74");
//对象序列化,对象people序列化后存redis
People people = new People("0", "张三", "30", "2019-01-17");
jedis.set("xmlobj", serialize(people));
String getByte = jedis.get("xmlobj");
People p = (People)deserizlize(People.class,getByte.toString());
System.out.println(p.getId()+"--"+p.getName()+"-"+p.getAge()+"-"+p.getBirthday());
System.out.println("------------------");
//集合序列化,把对象people 封装对象中的集合
People p1 = new People("1", "张三", "34", "2019-01-18");
People p2 = new People("2", "李四", "28", "2019-01-19");
List list = new ArrayList<>();
list.add(p1);
list.add(p2);
DataListWrapper dataListWrapper = new DataListWrapper(list);
jedis.set("xmllist", serialize(dataListWrapper));
String strList = jedis.get("xmllist");
DataListWrapper wrapper = (DataListWrapper)deserizlize(DataListWrapper.class,strList);
for (People pp : (List)wrapper.getDataList()) {
System.out.println(pp.getId()+"--"+pp.getName()+"-"+pp.getAge()+"-"+pp.getBirthday());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (jedis != null) {
jedis.close();
}
}
}
}
redis-cli中显示
192.168.172.74:6379> get xmlobj
"30 2019-01-17 0 \xe5\xbc\xa0\xe4\xb8\x89 "
192.168.172.74:6379> get xmllist
" "
192.168.172.74:6379>
控制台输出
0--张三-30-2019-01-17
------------------
1--张三-34-2019-01-18
2--李四-28-2019-01-19
参考:https://github.com/protocolbuffers/protobuf
参考:https://github.com/protocolbuffers/protobuf/blob/master/src/README.md
参考:https://www.cnblogs.com/BigJunOba/p/9127414.html
参考:https://blog.csdn.net/huanggang982/article/details/77944174