java 对象序列化的缺点_JAVASE序列化和反序列化讲解

本文探讨了序列化与反序列化的核心概念,包括标志性接口Serializable的作用,序列化版本号的重要性,以及如何处理类代码改动后的版本兼容问题。通过实例演示了如何正确处理transient关键字和自定义序列化版本。
摘要由CSDN通过智能技术生成

文章目录

序列化

常见问题

标志性接口

序列化版本号有什么用?

序列化对象

Student

main

实现截图

序列化集合

反序列化

反序列化对象

反序列化集合

判断反序列化是否是集合

反序列化集合代码

注意

transient

Student类

注意

反序列化代码

序列化代码

执行结果

name为NULL解释:

序列化版本号有什么用?

java语言中是采用什么机制来区分类的?

举例讲解

这种自动生成序列化版本号有什么缺陷?

IDEA自动生成序列化版本号

如下操作

最终结论:

04e9a3ecb1c3700063e894a6367a1ecd.png

序列化

1.序列化:Serialize java对象存储到磁盘文件中,将java对象的状态保存下来的过程

(ObjectOutputStream)(从内存到硬盘,也就是输出)

2.参与序列化和反序列化对象,必须实现Serializable接口,只是一个标志接口

java中有两种接口:

普通接口

标志接口(标志接口都是没有方法的)

常见问题

6526191e5a357466c16573032cda4be5.png

报错原因是:所需序列化的类对象并没有继承接口Serializable

e3b01a70e0fa5dccce7ecf66da2e4fe3.png

然后按下ctrl + b可以查看这个接口Serializable源码

d1fd8f8db764ab4e43081c16571a3ba2.png

标志性接口

很显然,这是一个标志性接口

注意:通过源代码发现,Serializable接口只是一个标志接口:

public interface Serializable{

}

那么这个接口中当中什么都没有

那么它到底起什么作用,标志的作用,java虚拟机看到这个类实现了这个接口,可能会对这个类进行特殊待遇。

Serializable这个标志接口是给java虚拟机参考的,java虚拟机看到这个接口之后,会为该类自动生成一个序列化版本号

序列化版本号有什么用?

过了很久,序列化原来那个类源代码改动了,

源代码改动了之后,需要重新编译,编译之后生成了全新的字节码文件

并且class文件再次运行的时候,java虚拟机生成的序列化版本号也会随之发生相应的改变

序列化对象

Student

import java.io.Serializable;

public class Student {

private int no;

private String name;

public Student(){

}

public Student(int no ,String name ){

this.name=name;

this.no=no;

}

public int getNo() {

return no;

}

public String getName() {

return name;

}

public void setNo(int no) {

this.no = no;

}

public void setName(String name) {

this.name = name;

}

@Override

public String toString() {

return "Student{" +

"no=" + no +

", name='" + name + '\'' +

'}';

}

}

main

import java.io.FileOutputStream;

import java.io.ObjectOutputStream;

import java.io.Serializable;

public class ObjectOutputStreamTest01 implements Serializable {

public static void main(String[] args) throws Exception{

//创建java对象

Student s=new Student(111,"豪哥");

//序列化

ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("students"));

//序列化对象

oos.writeObject(s);

//刷新

oos.flush();

//关闭

oos.close();

}

}

实现截图

5f8811b14bfba6759e9d8d6b212a4c5a.png

2cd806b6d5d7331a51900826891e1147.png

169ef0647ce7841129c35f837d5a2a7d.png

序列化集合

import java.io.FileOutputStream;

import java.io.ObjectOutputStream;

import java.util.ArrayList;

import java.util.List;

public class ObjectOutputStreamTest01 {

public static void main(String[] args) throws Exception{

//创建对象list集合

ListuserList=new ArrayList<>();

userList.add(new Student(1,"莫老板"));

userList.add(new Student(2,"戴总"));

userList.add(new Student(3,"彪彪"));

//序列化

ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("students"));

//序列化对象

oos.writeObject(userList);

//刷新

oos.flush();

//关闭

oos.close();

}

}

4e5e5e1bdd83d674d10367250c55d6f3.png

75ee760881854dfd625b4b699d81fc1a.png

反序列化

反序列化:DeSerialize 将硬盘上的数据重新恢复到内存当中,恢复成java对象(ObjectInputStream)(从硬盘到内存,也就是输入)

反序列化对象

import java.io.FileInputStream;

import java.io.ObjectInputStream;

public class ObjectInputStreamtTest01 {

public static void main(String[] args) throws Exception{

ObjectInputStream ois=new ObjectInputStream(new FileInputStream("students"));

//开始序列化,读

Object obj=ois.readObject();

//反序列化回来是一个学生对象,所以会调用学生对象的toString方法

System.out.print(obj);

ois.close();

}

}

20626a8fa4353a5c497ee24c995dab65.png

反序列化集合

判断反序列化是否是集合

首先利用代码判断,反序列的东西,是否是集合:

import java.io.FileInputStream;

import java.io.ObjectInputStream;

import java.util.List;

public class ObjectInputStreamtTest01 {

public static void main(String[] args) throws Exception{

ObjectInputStream ois=new ObjectInputStream(new FileInputStream("students"));

//开始序列化,读

Object obj=ois.readObject();

System.out.print(obj instanceof List);

ois.close();

}

}

383a9394246579b7d302d139f3ae4aa6.png

反序列化集合代码

import java.io.FileInputStream;

import java.io.ObjectInputStream;

import java.util.List;

public class ObjectInputStreamtTest01 {

public static void main(String[] args) throws Exception{

ObjectInputStream ois=new ObjectInputStream(new FileInputStream("students"));

ListuserList=(List) ois.readObject();

//反序列化回来是一个学生对象,所以会调用学生对象的toString方法

for(Student student:userList){

System.out.print(student);

}

ois.close();

}

}

37a9eea5c2c8072a42ab8cee2fe7e667.png

注意

如果不用集合,直接存多个对象,那么存第二个对象会报错的,需要使用List集合

transient

transient 关键字表示游离的,不参与序列化

Student类

import java.io.Serializable;

public class Student implements Serializable {

private int no;

transient private String name;

public Student(){

}

public Student(int no ,String name ){

this.name=name;

this.no=no;

}

public int getNo() {

return no;

}

public String getName() {

return name;

}

public void setNo(int no) {

this.no = no;

}

public void setName(String name) {

this.name = name;

}

@Override

public String toString() {

return "Student{" +

"no=" + no +

", name='" + name + '\'' +

'}';

}

}

注意

name前面加了个transient关键字

反序列化代码

import java.io.FileInputStream;

import java.io.ObjectInputStream;

import java.util.List;

public class ObjectInputStreamtTest01 {

public static void main(String[] args) throws Exception{

ObjectInputStream ois=new ObjectInputStream(new FileInputStream("students"));

ListuserList=(List) ois.readObject();

//反序列化回来是一个学生对象,所以会调用学生对象的toString方法

for(Student student:userList){

System.out.print(student);

}

ois.close();

}

}

序列化代码

import java.io.FileOutputStream;

import java.io.ObjectOutputStream;

import java.util.ArrayList;

import java.util.List;

public class ObjectOutputStreamTest01 {

public static void main(String[] args) throws Exception{

ListuserList=new ArrayList<>();

userList.add(new Student(1,"莫老板"));

userList.add(new Student(2,"戴总"));

userList.add(new Student(3,"彪彪"));

//创建java对象

Student s=new Student(4,"豪哥");

//序列化

ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("students"));

//序列化对象

oos.writeObject(userList);

//刷新

oos.flush();

//关闭

oos.close();

}

}

执行结果

当执行序列化后,再来执行反序列结果后:结果如下:

0d0072872fb41d5db340d39a99922751.png

name为NULL解释:

因为开始时,设置了name是不参加序列化的,所以name并没有序列化到文件中,但是最终在反序列化时,需要new对象,new三个user对象,name没有值,然后就为默认值NULL

序列化版本号有什么用?

过了很久,序列化原来那个类源代码改动了,

源代码改动了之后,需要重新编译,编译之后生成了全新的字节码文件

并且class文件再次运行的时候,java虚拟机生成的序列化版本号也会随之发生相应的改变

2cc075cd3298e05b688f42fe17ce787f.png

private static final long serialVersionUID=1L;

java语言中是采用什么机制来区分类的?

首先通过类名进行对比,如果类名不一样,肯定不是同一个类

如果类名一样,再怎么进行类的区别?靠序列化版本号进行区分

举例讲解

小鹏编写了一个类:com.bjpowernode.java.bean.Student implements Serializable

小李编写了一个类:com.bjpowernode.java.bean.Student implements Serializable

对于不同的人编写了同一个类,但这两个类确实不是同一个类。这个时候序列化版本就起上了作用。对于java虚拟机来说,java虚拟机是可以区分开这两个类的,因为这两个类都实现了Serializable接口,都有默认的序列化版本号,他们的序列化版本不一样,所以区分开了(这是自动生成序列化版本号的好处)

这种自动生成序列化版本号有什么缺陷?

不能修改原来的序列化类对象的代码,只要一修改代码,那么就无法反序列化(一旦代码确定之后,不能就行后续的修改,因为只要修改,必然会重新编译,此时就会生成全新的序列化版本号,这时候java虚拟机会认为这是个全新的类)

IDEA自动生成序列化版本号

3d12f1b75816accd46e3526188750fea.png

8c1e02ddc9fe13ac041b6e4f22f73630.png

4a91454e0ed1c6f0d9dfb0787f7e1977.png

f4156eb0b8d4922447954c8fa0c6d5dc.png

勾选之后呢,点击ok

如下操作

1316b884b24e7ea44b5d8c0bf033fa41.png

当这个类没有手动添加序列化版本时,把鼠标放在Student上,然后点击Alt+回车

715aa029952c0e0db3cd8dc7dce835e5.png

点击第一个后

6d02975be70f4f799e8547257dd1c942.png

就会自动生成一串序列化版本号喽

最终结论:

凡是实现了Serializable接口,建议给该类提供一个固定不变的序列化版本号。这样,以后这个类即使代码修改了,但是版本号不变,java虚拟机会认为是同一个类。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值