1、序列化的意义:
①如何远程传输对象(java对象)?
②序列化带来的性能如何优化?
个人理解:数据传输时(对象),需要通过序列化将对象转成二进制数据进行传输。再通过反序列化将二进制数据转化成对象。
什么是序列化与反序列化?
java允许在内存中创建可复用的java对象,但是只有jvm运行的时候,这个对象才存在。jvm停了,内存中的对象也没了。
序列化:把对象的状态信息转换为可存储、可传输的可视化数据。
反序列化:把可存储、可传输的可视化数据转换为java对象
序列化的作用:
1、服务器与服务器之前传输对象:ServerA序列化对象 远程通信传给ServerB,ServerB反序列化成对象。
2、服务器A 可以将对象序列化存储在硬盘中,也可以从硬盘上反序列化成对象
2、序列化面临的挑战:
①希望序列化以后内容越小越好,越小占用带宽,传输的更快
②序列化的计算过程需要消耗内存与资源。如何优化、选择
java本身提供了序列化的机制:Serializable
①序列化后的数据比较大
②语言限制(跨语言跨平台)
③基于xml(soap) 比java提供的二进制的方式更容易理解
④基于JSON格式 占用空间也很大、消耗性能高、效率低
直接上代码,java自带的序列化机制:
public interface MySerializable {
/**
* 序列化 对象转二进制byte数组
* @param obj 待序列化的java对象
* @return
*/
<T> byte[] serializable(T obj);
/**
* 反序列化 二进制byte数组转对象
* @param data 待反序列化的二进制
* @param clazz 待序列化生成的java对象Class
* @return
*/
<T> T deSerializable(byte[] data,Class<T> clazz);
/**
* 序列化对象存储在文件中
* @param obj 待序列化的java对象
* @return
*/
<T> void serializableToFile(T obj, String fileUrl);
/**
* 反序列化 从文件中读取
* @param data 待反序列化的二进制
* @param clazz 待序列化生成的java对象Class
* @return
*/
<T> T deSerializableByFile(String fileUrl,Class<T> clazz);
}
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* 基于jdk的方式提供序列化机制
*
* @author lemon
* @date 2021年8月18日
*/
public class JavaSerializable implements MySerializable {
@Override
public <T> byte[] serializable(T obj) {
// 序列化 就是将java对象转化为二进制 用流进行传输
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = null;
try {
objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(obj);
return byteArrayOutputStream.toByteArray();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭流
if (objectOutputStream != null) {
try {
objectOutputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 关闭流
try {
byteArrayOutputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
@Override
public <T> T deSerializable(byte[] data, Class<T> clazz) {
// 反序列化 就是将java对象的序列化二进制数据 转化为java对象
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data);
ObjectInputStream objectInputStream = null;
try {
objectInputStream = new ObjectInputStream(byteArrayInputStream);
try {
return (T)objectInputStream.readObject();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭流
if (objectInputStream != null) {
try {
objectInputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 关闭流
try {
byteArrayInputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
@Override
public <T> void serializableToFile(T obj,String fileUrl) {
// 序列化 就是将java对象转化为二进制 存储在文件中
ObjectOutputStream objectOutputStream = null;
try {
//写到项目根目录user文件中
objectOutputStream = new ObjectOutputStream(new FileOutputStream(fileUrl));
objectOutputStream.writeObject(obj);
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭流
if (objectOutputStream != null) {
try {
objectOutputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
@Override
public <T> T deSerializableByFile(String fileUrl, Class<T> clazz) {
// 反序列化 就是将文件中的java对象的序列化二进制数据 转化为java对象
ObjectInputStream objectInputStream = null;
try {
objectInputStream = new ObjectInputStream(new FileInputStream(fileUrl));
try {
return (T)objectInputStream.readObject();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭流
if (objectInputStream != null) {
try {
objectInputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return null;
}
}
import java.io.Serializable;
public class User implements Serializable{
/** serialVersionUID*/
private static final long serialVersionUID = 2L;
private String name;
private int age;
public User() {
}
public User(String name, int age) {
super();
this.name = name;
this.age = age;
}
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;
}
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + "]";
}
}
测试:
public class test {
public static void main(String[] args) {
MySerializable mySerializable = new JavaSerializable();
User user = new User("lemon",18);
/*
* 测试一
*/
// //序列化 对象序列化转换成byte数组
// byte[] bytes = mySerializable.serializable(user);
// //反序列化 序列化byte数组 转化为java对象
// User myUser = mySerializable.deSerializable(bytes, User.class);
// System.out.println(myUser);
//
/*
* 测试二
*/
//将java对象序列化 保存在文件里
//mySerializable.serializableToFile(user,"user");
//将文件中的 反序列化成java对象
//User myUser = mySerializable.deSerializableByFile("user", User.class);
//System.out.println(myUser);
/*
* 测试三
*/
//对同一个流的管道里写两次同一对象,输出结果,第二次在第一次增加五个字节(引用指向的对象)
//同时对一个对象的序列化两次
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("user"));
outputStream.flush();
outputStream.writeObject(user);
System.out.println(new File("user").length());
outputStream.writeObject(user);
outputStream.flush();
outputStream.close();
System.out.println(new File("user").length());
}
}
总结:
①需要序列化的实体类User必须实现Serializable接口,或者父类实现 Serializable接口。否则报错(下图)
②基于流的方式实现序列化(测试一)
③serialVersionUID 版本id(为了保证序列化的安全性,序列化与反序列化的id不同,则报错)
④父类的变量不会被序列化
⑤静态变量不会被序列化
⑤ transient 关键字修饰的变量 不会被序列化(private transient int age;)
⑥对同一对象同一个流序列化两次,不会重复写,只会在第一次的基础上增加五个字节的引用(测试三)
3、序列化的高阶认识
java自带的克隆:(浅克隆)
定义:只克隆这个对象本身和这个对象对应的值,但是这个对象里面的成员变量所指向的引用不再去克隆。
本身java的每个类都存在克隆功能:实现Cloneable接口,重写clone方法
序列化实现深克隆:
原理:序列化后的对象,是一个新的对象;
import java.io.Serializable;
/*
* 邮件
*
* @author lemon
* @date 2021年8月23日
*/
public class Email implements Serializable {
/**
* 邮件内容
*/
private String content;
public Email(String content) {
super();
this.content = content;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.Serializable;
/**
* 收件人
*
* @author lemon
* @date 2021年8月23日
*/
public class Person implements Cloneable,Serializable {
/**
* 发送人
*/
private String name;
/**
* 邮件内容
*/
private Email email;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Email getEmail() {
return email;
}
public void setEmail(Email email) {
this.email = email;
}
/**
* 重写克隆方法:浅克隆
*/
@Override
protected Person clone() throws CloneNotSupportedException {
return (Person)super.clone();
}
/**
* 深克隆
* @return Person
* @throws IOException
* @throws ClassNotFoundException
* @throws FileNotFoundException
*/
public Person deppClone() throws IOException, ClassNotFoundException {
//注意:这里并没有关闭流,可以自行添加关闭
//先序列化 当前类
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//在反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Person)ois.readObject();
}
}
import java.io.IOException;
public class CloneTest {
public static void main(String[] args) throws CloneNotSupportedException, ClassNotFoundException, IOException {
Email email = new Email("上海市今天有雨!!!");
Person p1 = new Person("小明");
p1.setEmail(email);
//浅克隆
Person p2 = p1.clone();
p2.setName("小红");
p2.getEmail().setContent("北京市今天晴天!!!");
System.out.println("浅克隆");
System.out.println(p1.getName() + "->" + p1.getEmail().getContent());
System.out.println(p2.getName() + "->" + p2.getEmail().getContent());
/*
* 浅克隆结果:
* 小明->北京市今天晴天!!!
* 小红->北京市今天晴天!!!
*/
//深克隆
Person p3 = p1.deppClone();
p3.setName("小光");
p3.getEmail().setContent("南京市今天多云!!!");
System.out.println("深克隆");
System.out.println(p1.getName() + "->" + p1.getEmail().getContent());
System.out.println(p3.getName() + "->" + p3.getEmail().getContent());
}
}
结果:
4、常见的序列化技术及应用
序列化后的结果大小、性能(利用cup的性能、内存使用效率)、序列化后的复杂度
XML形式:
优点:可读性高,方便阅读和调试。
缺点:序列化以后字节码较大、效率不高。
用webservice soap(http+xml) 很多
import com.lemon.MySerializable;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
/**
* 基于xml的序列化
*
* @author lemon
* @date 2021年8月23日
*/
public class XmlSerializable implements MySerializable{
XStream xstream = new XStream(new DomDriver());
/**
* 序列化
*/
@Override
public <T> byte[] serializable(T obj) {
return xstream.toXML(obj).getBytes();
}
/**
* 反序列化
*/
@Override
public <T> T deSerializable(byte[] data, Class<T> clazz) {
return (T)xstream.fromXML(new String (data));
}
@Override
public <T> void serializableToFile(T obj, String fileUrl) {
// TODO Auto-generated method stub
}
@Override
public <T> T deSerializableByFile(String fileUrl, Class<T> clazz) {
// TODO Auto-generated method stub
return null;
}
}
import com.lemon.MySerializable;
import com.lemon.User;
public class XmlTest {
public static void main(String[] args) {
MySerializable mySerializable = new XmlSerializable();
User user = new User("lemon",18);
//序列化当前对象
byte[] bytes = mySerializable.serializable(user);
System.out.println("序列化结果->" + new String(bytes));
//反序列化
User newUser = mySerializable.deSerializable(bytes, User.class);
System.out.println(newUser);
}
}
测试结果:
序列化框架:
JSON形式:
jackson(spring默认响应内容)
fastjson(阿里的,据说是最快的)
GSON:谷歌的
hessian2:dubbo默认使用(也可以自己选择)
Protobuf:序列化框架(比较新)
例子:fastjson用法
package com.lemon.fastJson;
import com.alibaba.fastjson.JSON;
import com.lemon.MySerializable;
public class FastJsonSerializer implements MySerializable{
@Override
public <T> byte[] serializable(T obj) {
return JSON.toJSONString(obj).getBytes();
}
@Override
public <T> T deSerializable(byte[] data, Class<T> clazz) {
return JSON.parseObject(new String(data), clazz);
}
@Override
public <T> void serializableToFile(T obj, String fileUrl) {
// TODO Auto-generated method stub
}
@Override
public <T> T deSerializableByFile(String fileUrl, Class<T> clazz) {
// TODO Auto-generated method stub
return null;
}
}
package com.lemon.fastJson;
import com.lemon.JavaSerializable;
import com.lemon.MySerializable;
import com.lemon.User;
public class FastJsonTest {
public static void main(String[] args) {
MySerializable mySerializable = new FastJsonSerializer();
User user = new User("lemon",18);
byte[] bytes = mySerializable.serializable(user);
System.out.println(new String(bytes));
User user1 = mySerializable.deSerializable(bytes, User.class);
System.out.println(user1);
}
}
Protobuf:序列化框架
基于谷歌的数据交换格式、开源的、
1、独立语言(多种语言都有实现)、独立平台
2、可以和各种传输层协议一起使用
3、序列化以后,空间开销和性能都比较好
4、解析性能也高
5、缺点:独立开发语言、独立编译器。需要学习成本
1、步骤:
①下载独立编译器
②编写独立的proto文件(proto自己的语法)
③编译完(编译成对应的java版本) 生成的类具备序列化和反序列化功能
6、序列化框架的选型