网站首页 > java教程 正文
什么是序列化?
一句话概括。序列化将对象的状态信息转换为可以存储或传输的形式的过程。而Java序列化,就是指把Java对象转换为“有序”字节序列的过程。相反的,把“有序”字节序列恢复为Java对象的过程称之为反序列化。
怎么理解上面的描述呢?从序列化的定义的可以看出,序列化其实是一个用来保障存储和传输的机制,而其针对的对象,肯定就是那些不便存储和传输的信息,而这体现在java编程中,就是类的实例 —— 类对象。
上面提到的“有序”,它并不是真的有顺序,其实是比较抽象化的表达,以此来表达经过序列化处理的对象,能够通过反序列化恢复成原来的样子这样一个过程
举个例子,你要搬家了,家里有个大鞋架,因为鞋架太大了,不便存储和运输,于是你把鞋机拆散了,然后把拆解步骤记录下来,运输到新家后,你再根据步骤记录把零件组装成鞋架原来的样子。拆鞋架和组装鞋架其实就是序列化和反序列的过程,而你记录的那份拆解步骤,就是序列化协议。
怎么实现序列化?
一、实现Serializable接口
定义一个类实现Serializable接口:
public class Rectangle implements Serializable {
private int width;
private int length;
public Rectangle(){}
public Rectangle(int width,int length){
this.width = width;
this.length = length;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
@Override
public String toString() {
return "Rectangle{" +
"width=" + width +
", length=" + length +
'}';
}
}
二、实现Externalizable接口
实现writeExternal、readExternal方法
public class Rectangle implements Externalizable {
private int width;
private int length;
public Rectangle(){}
public Rectangle(int width,int length){
this.width = width;
this.length = length;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
@Override
public String toString() {
return "Rectangle{" +
"width=" + width +
", length=" + length +
'}';
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
}
}
序列化和反序列化
public static void main(String[] args){
/* 序列化 */
Rectangle rectangle = new Rectangle(6,8);
try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("rectangle.out"))){ oos.writeObject(rectangle);
}
catch(Exception ex){ ex.printStackTrace();
}
/* 反序列化 */
try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream("rectangle.out")){
Rectangle rectangle1 = (Rectangle) ois.readObject();
System.out.println(rectangle1);
}catch(Exception ex){ ex.printStackTrace();
}
}
正常的输出结果:
Rectangle{width=6, length=8}
当实现Externalizable接口,没有对writeExternal和readExternal重写时,结果如下
Rectangle{width=0, length=0}
也就是说的,实现Externalizable接口,需要自己实现序列化和反序列逻辑,编程人员用比较高的灵活度,这点在接下来的自定义序列化会讲到
自定义序列化?
transient关键字
由transient关键字修饰的成员的变量,序列化时将被忽略。该方法决定变量是否序列化。
该自定义方法级别最低,当使用的下面的任一方法时,transient关键字将失效。换句话说,使用下面各方法进行序列化自定义,那么transient修饰的变量同样会被序列化。
自定义readObject和writeObject方法
通过的类中自定义readObject和writeObject方法,可以控制该类的序列化和反序列化逻辑,jvm在进行序列化的时候会自动调用readObject方法,反序列化调用writeObject。
private void readObject(ObjectInputStream ois) throws IOException {
this.length = ois.readInt() / 100;
this.width = ois.readInt();
}
private void writeObject(ObjectOutputStream oos)throws IOException{
this.length = this.length * 100;
oos.writeInt(this.length);
oos.writeInt(this.width);
}
自定义writeReplace方法
writeReplace方法没有入参,用于改变序列化对象的类型,且会使用默认的序列化机。也就是说,上面提到的自定义readObject和writeObject方法都将失效。以下为实例:
private Object writeReplace() throws ObjectStreamException {
List<Object> list = new ArrayList<>();
list.add(this.length * 1000);
list.add(this.width *1000);
return list;
}
相应的,在进行反序列化的时候,就应该使用新的类型进行接收,如该例子,应该用List<Object>接收反序列化对象。
自定义readResolve方法
readResolve方法用于替换反序列化出来的对象,该方法同样没有入参。
private Object readResolve() throws ObjectStreamException {
System.out.println("自定义readResolve方法被调用");
return new Rectangle(9,10);
}
由于没发拿到序列化的信息,因此常用来控制单例模式下,反序列化出来的对象为原先的单例对象,以维护单例模式中反序列化对象的单一性。
实现Externalizable接口
这个已经在上面实例化方式中有所提及。实现该接口必须强制实现writeExternal和readExternal 两个方法如下:
@Override
public void writeExternal(ObjectOutput out) throws IOException {
this.length = this.length * 100;
out.writeInt(this.length);
out.writeInt(this.width);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.length = in.readInt() / 100;
this.width = in.readInt();
}
该效果等同于上面实现Serializable接口,然后自定义readObject和writeObjectf方法
序列化的注意事项
- serialVersionUID 序列化版本号用来区分需要序列化类的版本。假如序列化的类中发生了变量的修改,那么该版本号一般也要跟着修改,才能够在反序列化的时候得到对应版本的类对象,否则会抛出InvalidClassException异常
- 序列化与类的初始化一样,是的一个递归的过程。当一个类实现了序列化接口,该类的成员除了基本类型和String类型之外,其他的引用类型也必须是可序列化的;否则会抛NotSerializableException异常
- 同一对象多次序列化只会序列化一次如果程序对同一个对象有多次序列化,那么只会在第一次进行序列化,后续实际上是返回一个编号进行区分
- 反序列化的顺序与序列化时的顺序一致,类似队列模型
序列化的使用场景
讲了这么多,那序列化到底用在什么地方呢?
由jvm的内存结构相关知识我们可以知道,java对象都被保存在堆内存中。在jvm处于运行状态的时候,我们能够对对象的进行复用。但一旦jvm生命周期结束,相关对象也随之被回收。也就是说的,如果希望在jvm停止后还能够拿到某些对象,这时候就需要用到java的序列化。
因此大概由以下几种场景会使用到java的序列化
当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;当你想用套接字在网络上传送对象的时候;当你想通过RMI(远程方法调用)传输对象的时候;
分类: [Java基础]
标签: [java]
猜你喜欢
- 2024-10-08 Java核心知识 基础六 JAVA序列化(创建可复用的Java对象)
- 2024-10-08 java中的序列化(JAVA中的序列化)
- 2024-10-08 Java 面试题之 序列化和反序列化的深入理解
- 2024-10-08 Oracle:Java 的序列化就是个错误,我们要删掉它!
- 2024-10-08 java序列化和反序列化实例详解(java序列化和反序列化实例详解区别)
- 2024-10-08 java对象序列化(java对象序列化到文件)
- 2024-10-08 实战案例:轻松搞定Java两种序列化机制
- 2024-10-08 java序列化机制之protobuf(快速高效跨语言)
- 2024-10-08 Java路径-40-Java序列化(未检测到java sdk环境请在配置中添加sdk安装路径)
- 2024-10-08 java序列化知多少(java序列化怎么实现)
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)