java中的序列化与反序列化

序列化与反序列化

一 、什么是序列化与反序列化
把对象转换为字节序列的过程称为对象的序列化。
把字节序列恢复为对象的过程称为对象的反序列化

  • 一个对象只要实现了Serilizable接口,这个对象就可以被序列化,java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过程,只要这个类实现了Serilizable接口,这个类的所有属性和方法都会自动序列化。

为什么需要实现Serilizable接口?

  • 如果一个类想被序列化,需要实现Serializable接口。否则将抛出NotSerializableException异常,这是因为,在序列化操作过程中会对类型进行检查,要求被序列化的类必须属于Enum、Array和Serializable类型其中的任何一种,这也是为什么Serializable虽然是一个空接口,但是只要实现了该接口就能序列化和反序列化。

二、为什么需要序列化与反序列化?

  • 当两个进程进行远程通信时,可以相互发送各种类型的数据,包括文本、图片、音频、视频等, 而这些数据都会以二进制序列的形式在网络上传送。当两个Java进程进行通信时,能否实现进程间的对象传送呢?如何做到呢?此时就需要Java序列化与反序列化了!发送方需要把这个Java对象转换为字节序列,然后在网络上传送。接收方需要从字节序列中恢复出Java对象。

序列化的好处

  • 实现了数据的持久化,通过序列化可以把数据永久地保存到硬盘上(通常存放在文件里)
  • 用序列化实现远程通信,即在网络上传送对象的字节序列。

三、序列化特点

  1. 序列化时,只对对象的状态进行保存,而不管对象的方法。
  2. 当一个父类实现序列化时,子类自动实现序列化,不需要显示实现Serializable接口
  3. 当一个对象的实例变量引用了其他对象时,序列化该对象时,也把引用对象序列化(可以理解成,在这里引用对象只是当做类的属性,无特殊说明自然会把属性序列化)
  4. 对象中,被static或transient(对象的临时数据)修饰的变量,不会被序列化。

四、如何序列化和反序列化
利用ObjectInputStream(对象输入流)和ObjectOutputStream(对象输出流)

  1. 序列化:ObjectInputStream(对象输入流)
FileOutputStream fileOutputStream=new FileOutputStream(数据保存目录);
ObjectOutputStream out=new ObjectOutputStream(fileOutputStream);//对象输入流,得到的对象序列化,把得到的字节序列写到目标输入流中
out.writeObject(序列化对象); //通过对象输入流的writeObject方法写对象

例子:Employee.java

//一个对象可序列化,需要实现Serilizable接口
public class Employee implements Serializable {
    public String name;
    public static String address;
    public transient String password;
    public int number;
    public Employee(String name,String address,String password,int number)
    {
        this.name=name;
        this.address=address;
        this.password=password;
        this.number=number;
    }
}

序列化类 SerializeDemo.java

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class SerializeDemo {
    public static void main(String[] args)
    {
        Employee employee=new Employee("xiao","shenqiu","12345",1);
        try {
            FileOutputStream fileOutputStream=new FileOutputStream("/object.txt");
            ObjectOutputStream out=new ObjectOutputStream(fileOutputStream);
            try {
                out.writeObject(employee);
            }
            catch (Exception ee)
            {
                ee.printStackTrace();
            }
            out.close();
            fileOutputStream.close();
            System.out.println("序列化数据已保存在文件中");
        }
        catch (IOException i)
        {
            i.printStackTrace();
        }
    }
}

序列化类运行结果:

序列化数据已保存在文件中

Process finished with exit code 0

用UltraEdit 打开obiect.txt文件可以看到文件内容。对象已经序列化成字节序列存储在文件中了。其中***h代表行。
在这里插入图片描述

  • 2.反序列化

利用对象输入流ObjectInputStream

FileInputStream fileIn = new FileInputStream(数据读取目录);
ObjectInputStream in = new ObjectInputStream(fileIn);// 对象输入流
e = (Employee) in.readObject();//通过对象输入流的readObject()方法读取对象
反序列化文件 DeserializeDemo.java

import java.io.*;
public class DeserializeDemo {
    public static void main(String [] args)
    {
        Employee e = null;
        try
        {
            FileInputStream fileIn = new FileInputStream("/object.txt");
            ObjectInputStream in = new ObjectInputStream(fileIn);
            e = (Employee) in.readObject();
            in.close();
            fileIn.close();
        }catch(Exception i)
        {
            i.printStackTrace();
            return;
        }
        System.out.println("Deserialized Employee...");
        System.out.println("Name: " + e.name);
        System.out.println("Address: " + e.address);
        System.out.println("Password: " + e.password);
        System.out.println("Number: " + e.number);
    }
}

反序列化运行结果:

Deserialized Employee...
Name: xiao
Address: null
Password: null
Number: 1

Process finished with exit code 0

这里可以看到Employee 的属性(static)Address和(transient)Password没有被序列化,是null。
五、Serialversion 序列化版本号
(1)提高运行效率。如果在类中没有显式声明Serialversion。那么在序列化的时候回通过计算得到该值。显式声明可以省略计算。
(2)如果类没有提供SerialversionUID,那么编译器会自动生成。SerialversionUID就是对象的hashcode。如果加入新的成员变量,那么重新生成的SerialversionUID会变化。这样反序列的时候crash,产生java.io.InvalidClassException异常,不能正常的反序列化。

例子:
我们在Employee.java中加一个属性 i,再次运行反序列化文件DeserializeDemo.java

Employee .java

import java.io.Serializable;
public class Employee implements Serializable {
    public String name;
    public static String address;
    public transient String password;
    public int number;
    public int i=0;//新加属性i
    public Employee(String name,String address,String password,int number)
    {
        this.name=name;
        this.address=address;
        this.password=password;
        this.number=number;
    }
}

运行结果:

java.io.InvalidClassException: 序列化.Employee;local class incompatible:
stream classdesc serialVersionUID = -7599142119717411520, 
local class serialVersionUID = 5676476655507316830

可以看到序列化版本号不一致,导致不能反序列化,所以最好指定serialVersionUID。
我们指定serialVersionUID,看一下运行结果。
我们在Employee .java中指定serialVersionUID,运行SerializeDemo.java序列化,然后在文件Employee.java中添加属性 i,看一下反序列化的结果.

  1. 指定serialVersionUID。即修改Employee .java
  2. 序列化。即运行SerializeDemo .java
  3. 修改对象属性。即修改Employee.java
  4. 反序列化。DeserializeDemo.java
Employee .java

import java.io.Serializable;
public class Employee implements Serializable {
    public String name;
    public static String address;
    public transient String password;
    public int number;
    public int i=0;   //新加属性i
    private static final long serialVersionUID = -8976532647273106745L;//指定serialVersionUID
    public Employee(String name,String address,String password,int number)
    {
        this.name=name;
        this.address=address;
        this.password=password;
        this.number=number;
    }
}

运行DeserializeDemo.java文件

Deserialized Employee...
Name: xiao
Address: null
Password: null
Number: 1

可以看到此时反序列化正确。指定serialVersionUID ,那么修改序列化对象属性,反序列化时也不会出错。

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值