Java序列化和反序列化(详解)_洋嚎的博客-程序员秘密_java序列化及反序列化

技术标签: java  开发语言  

一、理解Java序列化和反序列化

Serialization(序列化)将java对象以一连串的字节保存在磁盘文件中的过程,也可以说是保存java对象状态的过程。序列化可以将数据永久保存在磁盘上(通常保存在文件中)。

deserialization(反序列化)将保存在磁盘文件中的java字节码重新转换成java对象称为反序列化。

二、序列化和反序列化的应用

两个进程在远程通信时,可以发送多种数据,包括文本、图片、音频、视频等,这些数据都是以二进制序列的形式在网络上传输。

java是面向对象的开发方式,一切都是java对象,想要在网络中传输java对象,可以使用序列化和反序列化去实现,发送发需要将java对象转换为字节序列,然后在网络上传送,接收方收到字符序列后,会通过反序列化将字节序列恢复成java对象。

java序列化的优点:

  • 实现了数据的持久化,通过序列化可以把数据持久地保存在硬盘上(磁盘文件)。
  • 利用序列化实现远程通信,在网络上传输字节序列。

三、序列化和反序列化地实现

1.JDK类库提供的序列化API:

  • java.io.ObjectOutputStream    
    表示对象输出流,其中writeObject(Object obj)方法可以将给定参数的obj对象进行序列化,将转换的一连串的字节序列写到指定的目标输出流中。
  • java.io.ObjectInputStream
    该类表示对象输入流,该类下的readObject(Object obj)方法会从源输入流中读取字节序列,并将它反序列化为一个java对象并返回。

序列化要求:

实现序列化的类对象必须实现了Serializable类或Externalizable类才能被序列化,否则会抛出异常。

实现java序列化和反序列化的三种方法:

现在要对student类进行序列化和反序列化,遵循以下方法:

方法一:若student类实现了serializable接口,则可以通过objectOutputstream和objectinputstream默认的序列化和反序列化方式,对非transient的实例变量进行序列化和反序列化。

方法二:若student类实现了serializable接口,并且定义了writeObject(objectOutputStream out)和

readObject(objectinputStream in)方法,则可以直接调用student类的两种方法进行序列化和反序列化。

方法三:若student类实现了Externalizable接口,则必须实现readExternal(Objectinput in)和writeExternal(Objectoutput out)方法进行序列化和反序列化。

JDK类库中的序列化步骤:

第一步:创建一个输出流对象,它可以包装一个输出流对象,如:文件输出流。

ObjectOutputStream out = new ObjectOutputStream(new fileOutputStream("E:\\JavaXuLiehua\\Student\\Student1.txt"));

 第二步:通过输出流对象的writeObject()方法写对象

out.writeObject("hollo word");

out.writeObject("happy")

 JDK中反序列化操作:

第一步:创建文件输入流对象

 ObjectInputStream in = new ObjectInputStream(new fileInputStream("E:\\JavaXuLiehua\\Student\\Student1.txt"));

 第二步:调用readObject()方法

 String obj1 = (String)in.readObject();

 String obj2 = (String)in.readObject();

 为了保证正确读取数据,对象输出流写入对象的顺序与对象输入流读取对象的顺序一致。

 Student类序列化和反序列化演示:

1.先创建一个继承了serializable类的student类

import java.io.Serializable;            //导入io包下的序列化类

//创建实现序列化接口的学生类
public class Student implements Serializable {
    //私有化成员变量
    private String name;
    private  char sex;
    private  int year;
    private  double gpa;

    public Student(){   //无参构造
    }
    public Student(String name,char sex,int year,double gpa){
        //参数给属性赋值
        this.name = name;
        this.sex = sex;
        this.year = year;
        this.gpa = gpa;
    }

    //重写set和get
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public double getGpa() {
        return gpa;
    }

    public void setGpa(double gpa) {
        this.gpa = gpa;
    }
}

把Student类的对象序列化到txt文件(E:\\JavaXuLiehua\\Student\\Student1.txt)中,并对文件进行反序列化:

import java.io.*;
import java.io.Externalizable;
/*
把student类对象序列化到文件E:\\JavaXuLiehua\\Student\\Student1.txt
 */
public class UserStudent {
    public static void main(String[] args) throws IOException {
        Student st = new Student("Tom",'M',20,3.6);         //实例化student类
        //判断Student1.txt是否创建成功
        File file = new File("E:\\JavaXuLiehua\\Student\\Student1.txt");
        if(file.exists()) {
            System.out.println("文件存在");
        }else{
            //否则创建新文件
            file.createNewFile();
        }
        try {
            //Student对象序列化过程
            FileOutputStream fos = new FileOutputStream(file);
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            //调用 ObjectOutputStream 中的 writeObject() 方法 写对象
            oos.writeObject(st);
            oos.flush();        //flush方法刷新缓冲区,写字符时会用,因为字符会先进入缓冲区,将内存中的数据立刻写出
            fos.close();
            oos.close();

            //Student对象反序列化过程
            FileInputStream fis = new FileInputStream(file);
            //创建对象输入流
            ObjectInputStream ois = new ObjectInputStream(fis);
            //读取对象
            Student st1 = (Student) ois.readObject();           //会抛出异常(类找不到异常)
            System.out.println("name = " + st1.getName());
            System.out.println("sex = " + st1.getSex());
            System.out.println("year = " + st1.getYear());
            System.out.println("gpa = " + st1.getGpa());
            ois.close();
            fis.close();
        }catch (ClassNotFoundException e){
            e.printStackTrace();
        }
    }
}

 查看txt文件,结果如下:

 sr JavaxulieHua.Studentd9Q藿Hf D gpaC sexI yearL namet Ljava/lang/String;[email protected] 烫烫掏 M   t Tom

 可以看出其中的内容是不容易阅读的,只能通过反序列化读取。

四、transient关键字

transient关键字表示有理的,被修饰的数据不能进行序列化

这里不做详细介绍,修改情况如下:

private transient char sex;         //被transient关键字修饰,不参与序列化

 运行结果如下:

文件存在
name = Tom
sex =  
year = 20
gpa = 3.6

此时可以看见,被transient关键字修饰的变量sex并没有被序列化,返回了空值。

五、Externalizable接口实现序列化与反序列化

Externalizable接口继承Serializable接口,实现Externalizable接口需要实现readExternal()方法和writeExternal()方法,这两个方法是抽象方法,对应的是serializable接口的readObject()方法和writeObject()方法,可以理解为把serializable的两个方法抽象出来。Externalizable没有serializable的限制,static和transient关键字修饰的属性也能进行序列化。

具体代码实现如下:

复制对象student命名为student1,在里面重写writeExternal()方法和readExternal()方法,如下:

   @Override
    //对抽象方法进行重写
    public void writeExternal(ObjectOutput out) throws IOException{
        out.writeObject(name);
        out.writeObject(sex);
        out.writeObject(year);
        out.writeObject(gpa);
    }
    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        name = (String) in.readObject();
        sex = (char) in.readObject();
        year = (int) in.readObject();
        gpa = (double) in.readObject();
    }

相应的测试方法里面调用这两种方法的时候,直接调用writeObject()方法和readObject()方法即可,重写的writeExternal()和readExternal()方法会自动执行。

FileOutputStream fos1 = new FileOutputStream(file1);
                ObjectOutputStream oos1 = new ObjectOutputStream(fos1);
                //调用 ObjectOutputStream 中的 writeObject() 方法 写对象
                oos1.writeObject(st);       //会自动执行重写的writeExternal()方法
FileInputStream fis1 = new FileInputStream(file1);
                //创建对象输入流
                ObjectInputStream ois1 = new ObjectInputStream(fis1);
                //读取对象
                //会自动执行readExternal()方法
                Student1 st1 = (Student1) ois1.readObject();           //会抛出异常(类找不到异常)

虽然student1类里的sex属性被static或transient修饰,但依旧被序列化,结果如下:

文件存在
name = Tom
sex = M
year = 20
gpa = 3.6

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_62414755/article/details/125886742

智能推荐

21条SQL Server数据库开发经验_轻鸿飘羽的博客-程序员秘密

<br />一、了解你用的工具 <br />不要轻视这一点,这是我在这篇文章中讲述的最关键的一条。也许你也看到有很多的SQL Server程序员没有掌握全部的T-SQL命令和SQL Server提供的那些有用的工具。 <br />“什么?我要浪费一个月的时间来学习那些我永远也不会用到的SQL命令???”,你也许会这样说。对的,你不需要这样做。但是你应该用一个周末浏览所有的T-SQL命令。在这里,你的任务是了解,将来,当你设计一个查询时,你会记起来:“对了,这里有一个命令可以完全实现我需要的功能”,于是,到M

python 模拟键盘鼠标输入_Python模拟键盘输入和鼠标操作_weixin_39610759的博客-程序员秘密

一、Python键盘输入模拟:import win32apiimport win32conwin32api.keybd_event(17,0,0,0) #ctrl键位码是17win32api.keybd_event(86,0,0,0) #v键位码是86win32api.keybd_event(86,0,win32con.KEYEVENTF_KEYUP,0) #释放按键win32api.keyb...

Android studio:Failed to resolve: junit:junit:4.12解决办法_Cynthia.Chen的博客-程序员秘密

问题实际是无法连接到代码仓库,进而无法resolve,解决办法是修改代码仓库地址,即在app/build.gradle文件添加如下代码:allprojects {repositories {// jcenter()maven { url ‘http://repo1.maven.org/maven2’ }}}重新sync project即可...

tensorflow 打印每个tensor的参数量_TechOnly1988的博客-程序员秘密_tensorflow输出参数个数

定义完graph后面加上 from functools import reduce from operator import mul def get_num_params(): num_params = 0 for variable in tf.trainable_variables(): shape = varia...

【论文笔记】投影仪-相机系统标定方法_JiayuZ128的博客-程序员秘密_投影仪标定

Simple, Accurate, and Robust Projector-Camera Calibration论文地址Abstract标定的意义:结构光三维重建的精度很大程度上取决于相机和投影仪的精确标定。**存在的问题 **:虽然相机和投影仪可以由同一数学模型描述,但是现在还不太清楚如何改进相机标定的方法以用于投影仪的标定上。导致现在的投影仪标定方法用的都是简化的模型,忽略了镜头畸变,造成了精度的损失。论文贡献:提出一种新方法,用于估计在投影仪图像平面上三维点的图像坐标。方

5.全志H3-第一版核心板_Jun626的博客-程序员秘密

上面是我创建的群聊,欢迎新朋友的加入。近期工作原因,经常在加班,进度有点点慢。11月14号决定的要开始做核心板,一直到了11月21号才折腾完14号开始动工考虑到成本和效率的问题,决定用四层板,在AD上画这个板子首先是拉DDR的走线,距离先拉长点,避免后面走不下线拉完之后是这个样子,有折腾了一阵子最麻烦的搞完了,开始走其他的线,相对来说容易点,先走完EMMC的线15号进度把其他的线全部折腾上去看一下3D图这是15号的进度,后面加班变多..

随便推点

Unity中关于Outline和Shadow同时赋值颜色的问题_寒风狂欢的博客-程序员秘密

在Unity中,使用UGUI同时赋值Outline和Shadow颜色时出现异常的问题探究。

Ubuntu18.04 小米游戏本最早一代 双硬盘 安装 过程记录_東海林的博客-程序员秘密

Ubuntu18.04 小米游戏本最早一代 双硬盘 安装 过程记录。踩了很多坑,折腾了无数次,总结一下,方便日后查阅。UEFI+GPT新买了一个1T的西数SN550,779元。疫情期间,价格大涨,可又不得不用。0.1 我将原来的机械盘拆下来了,因为强行加装SSD温度有点高,而且SSD会弯曲。我把机械盘里的程序都复制到了新盘,并更改了盘符。用Windows10的磁盘管理即可。所以,Wi...

Git常用操作(日常工作使用)_Ss#plus的博客-程序员秘密

日常工作中常用经常用到的 Git操作

光猫机顶盒有路由鸿蒙吗,光猫和机顶盒是一样的吗_weixin_39867662的博客-程序员秘密

提到光猫或者机顶盒,很多人都了解是用于上网的,光猫和机顶盒是否是一样的,两者之间有没有什么区别?一、光猫和机顶盒是一样的吗1、两者是不一样的,因为两者有一定的区别,不是同一个类型。光猫能够将光信号转变为能够接受到的网络信号,再通过路由器,从而传输给电脑,用于家庭上网使用。2、机顶盒其实是一种网络机顶盒,安装于光猫之后,能够将网络信号传输到电视当中,这样子就能够在电视当中收到信号。如果没有光纤猫,只...

Android应用开发之所有动画使用详解_harrain的博客-程序员秘密

出处:http://blog.csdn.net/yanbober/article/details/46481171#t1题外话:有段时间没有更新博客了,这篇文章也是之前写了一半一直放在草稿箱,今天抽空把剩余的补上的。消失的这段时间真的好忙,节奏一下子有些适应不过来,早晨七点四十就得醒来,晚上九点四十才准备下班,好像最近都与世隔离了一样;然而自己每天还要熟悉一大套自己不熟悉、各种协议差异

推荐文章

热门文章

相关标签