序列化与反序列化

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) 很多

xstream的jar包点击获取

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、序列化框架的选型

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值