Java之序列化

概念

  • 序列化是将对象转换为字节序列的过程(其中可以是二进制、特定的字符串、xml、json)。
  • 反序列化是将字节序列恢复为对象的过程。

序列化使用场景

  • 需要把内存中的对象保存到文件中或者数据库中(持久化)时候。
  • 跨平台通过网络传输对象时候(WebService SOAP)。
  • 通过RNI传输对象的时候 (仅限于java环境)。

实现序列化

实现序列化比较简单将需要序列化的类实现Serializable即可。但是查看源码知道,Serializable这个接口中没有方法,所以我们即可理解为一个标记,标记该类是可以被序列化的。

序列化实例

复制代码
//对象类
import java.io.Serializable;

public class Student implements Serializable {

	private static final long serialVersionUID = -1556557458139596779L;
	private String name;
	private int age;
	
	public Student(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 "Student [name=" + name + ", age=" + age + "]";
	}
}


//序列化方法
public static void serialize(Object obj,String outFile){
	try {
		ObjectOutputStream oos = 
			new ObjectOutputStream(new FileOutputStream(outFile));
		//往磁盘中写入当前内存中对象的状态 
		oos.writeObject(obj);
		oos.flush();
		oos.close();
	} catch (IOException e) {
		e.printStackTrace();
	}

}


//反序列方法
public static Object deSerialize(String readFile){
	try {
		ObjectInputStream ois = 
			new ObjectInputStream(new FileInputStream(readFile));
		Object obj =ois.readObject();
		ois.close();
		return obj;
	} catch (IOException | ClassNotFoundException e) {
		e.printStackTrace();
	}
	return null;
}

//调用
Student s = new Student("KKY",22);
String sFileName = "student.out";
serialize(s,sFileName);

Student sde=(Student) deSerialize("student.out");
System.out.println(sde);

默认序列化机制

如果仅仅只是让某个类实现Serializable接口,而没有其它任何处理的话,则就是使用默认序列化机制。但是默认序列化机制效率非常低,所以一般是不使用java默认序列化规则。

序列化ID

复制代码
private static final long serialVersionUID = -1556557458139596779L;

序列化ID主要针对跨服务器、跨平台情况,如果序列化ID不一致就会无法反序列化,所以一般我们都需要去生成序列化ID。

其他注意点

指定不序列化

在属性上添加transient关键字,即可指明该属性不被序列化,同时静态变量不能被序列化。但是如果想序列化transient修饰的变量,就需要自定义序列化规则。虽然Serializable没有定义方法,但是有三个方法可以自己实现来修改默认序列化规则:

复制代码
private void writeObject(java.io.ObjectOutputStream out)
    throws IOException
private void readObject(java.io.ObjectInputStream in)
    throws IOException, ClassNotFoundException;
private void readObjectNoData()
    throws ObjectStreamException;

所以即可修改如下:

复制代码
transient private int age;


//JDK自动调用 扩展序列化规则一个方法
private void writeObject(ObjectOutputStream out) throws IOException{
	out.defaultWriteObject();
	out.writeInt(age);
}

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{
	in.defaultReadObject();
	age = in.readInt();
}

上述也是可以序列化age属性,不过这两个方法应该用于扩展序列化规则。

readResolve()方法

当我们使用单例模式时,本来是期望只生成一个对象实例,不过如果该类是可序列化的,那么情况就不一样了此时,我们可以增加对readResolve方法的实现,来确保在同一个JVM中只有一个单例对象的使用。

复制代码
private Student() { 
	super();
}  
 
private static final Student INSTANCE = new Student();  

public static Student getInstance() {
  	return INSTANCE;
}  

private Object readResolve()
 throws ObjectStreamException {  
  return INSTANCE;   
 }  

Externalizable接口

如果想自定义序列化内容可以实现Externalizable接口。实现Externalizable接口需要自己定义序列化和反序列化方法。

复制代码
mport java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

//JDK将不会调用默认的序列化规则
//完全使用自定义序列化规则
public class Person implements Externalizable {

	private String name;
	private int age;
	
	
	public Person() {
		super();
		// TODO Auto-generated constructor stub
	}
	public Person(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 "Person [name=" + name + ", age=" + age + "]";
	}
	//扩展序列化的方法
	@Override
	public void readExternal(ObjectInput in)
	 throws IOException, ClassNotFoundException {
		name = (String) in.readObject();
		age = in.readInt();
	}
	@Override
	public void writeExternal(ObjectOutput out)
	 throws IOException {
		out.writeObject(name);
		out.writeInt(age);
		
	}
}

序列化前和序列化后的对象的关系

深复制。

网络环境中的序列化

复制代码
java环境RMI Remote Method Invoker 远程方法调用
	仅限于java平台中调用
	序列化的效率非常低
	
	
WebService SOAP Simple Object Access Protrol 简单对象传出协议
	跨语言、跨平台
	还是没有解决效率低的问题
	可读性非常差,基于xml传输的
	
JSON
	跨语言、跨平台
	可读性非常强
	序列化效率有所提高,但目前来说还不是最高的

序列化发展方向

复制代码
主要发展方向其实就是往更高性能和更小的传输大小。

参考引用

https://blog.csdn.net/lingzhm/article/details/45047765

相关推荐
程序员清风37 分钟前
Java 后端高并发设计:线程池、限流、熔断与降级
java·数据库·oracle
李少兄37 分钟前
JavaScript 数据类型完全指南
开发语言·javascript·ecmascript
Seoyoneh1 小时前
Agentic Workflow编排架构:云客服从“被动响应”迈向“主动执行”的技术实现
java·开发语言·架构
海南java第二人1 小时前
对外 API 如何保证幂等性?插入数据 / 上传表格场景全方案汇总
java·幂等性
tryxr1 小时前
Chat2Excel 文件服务模块上传文件功能开发
java·项目·oss·easyexcel·文件服务
Tangyuewei1 小时前
Java 写 Agent:模型只是组件
java·开发语言
小玮看世界2 小时前
[Python]从合并区间到传感器融合区:合并区间在传感器区域融合的实际落地
开发语言·python
彧azz2 小时前
Java学习语法篇:变量
java·学习
繁星蓝雨3 小时前
C++的设计与演进大纲(如何从C语言一步步成长为C++)
c语言·开发语言·c++·c++历史·c++演进
Evand J3 小时前
【MATLAB例程,图像降噪滤波9】自适应维纳滑动窗口滤波(Wiener)图像降噪、方法对比,有中文注释
开发语言·图像处理·计算机视觉·matlab·滑动窗口·滤波·降噪