zoukankan      html  css  js  c++  java
  • 十二、序列化与反序列化(对象流)

    一、初步认识

    • 当两个进程远程通信时,彼此可以发送各种类型的数据。 无论是何种类型的数据,都会以二进制序列的形式在网络上传送。比如,我们可以通过http协议发送字符串信息;我们也可以在网络上直接发送Java对象。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象才能正常读取。
    • 把Java对象转换为字节序列的过程称为对象的序列化。把字节序列恢复为Java对象的过程称为对象的反序列化。

    二、对象序列化的作用有如下两种:

     1. 持久化: 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中,比如:休眠的实现。以后服务器session管理,hibernate将对象持久化实现。

     2. 网络通信:在网络上传送对象的字节序列。比如:服务器之间的数据通信、对象传递。

    三、什么场景会涉及序列化和反序列化的概念

      在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些session先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。

      当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。

    四、Java如何实现序列化和反序列化

    1、JDK类库中序列化和反序列化API

    (1)java.io.ObjectOutputStream:表示对象输出流;

    它的writeObject(Object obj)方法可以对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中;

    (2)java.io.ObjectInputStream:表示对象输入流;

    它的readObject()方法源输入流中读取字节序列,再把它们反序列化成为一个对象,并将其返回;

    2、实现序列化的要求

    只有实现了Serializable或Externalizable接口的类的对象才能被序列化,否则抛出异常!

    3、JDK类库中序列化的步骤

    步骤一:创建一个对象输出流,它可以包装一个其它类型的目标输出流,如文件输出流:

    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\object.out"));

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

    oos.writeObject(new User("xuliugen", "123456", "male"));

    4、JDK类库中反序列化的步骤

    步骤一:创建一个对象输入流,它可以包装一个其它类型输入流,如文件输入流:

    ObjectInputStream ois= new ObjectInputStream(new FileInputStream("object.out"));

    步骤二:通过对象输出流的readObject()方法读取对象:

    User user = (User) ois.readObject();

    说明:为了正确读取数据,完成反序列化,必须保证向对象输出流写对象的顺序与从对象输入流中读对象的顺序一致。

    序列化和反序列化的示例一:

     1 import java.io.BufferedInputStream;
     2 import java.io.BufferedOutputStream;
     3 import java.io.ByteArrayInputStream;
     4 import java.io.ByteArrayOutputStream;
     5 import java.io.IOException;
     6 import java.io.ObjectInputStream;
     7 import java.io.ObjectOutputStream;
     8 import java.util.Date;
     9 
    10 /**
    11  * 对象流:
    12  * 1、写出后读取
    13  * 2、读取的顺序与写出保持一致
    14  * 3、不是所有的对象都可以序列化Serializable
    15  *
    16  * ObjectOutputStream
    17  * ObjectInputStream
    18  *
    19  *
    20  */
    21 public class ObjectTest {
    22 
    23     public static void main(String[] args) throws IOException, ClassNotFoundException {
    24         //写出 -->序列化
    25         ByteArrayOutputStream baos =new ByteArrayOutputStream();
    26         ObjectOutputStream oos =new ObjectOutputStream(new BufferedOutputStream(baos));
    27         //操作数据类型 +数据
    28         oos.writeUTF("编码辛酸泪");
    29         oos.writeInt(18);
    30         oos.writeBoolean(false);
    31         oos.writeChar('a');
    32         //对象
    33         oos.writeObject("谁解其中味");
    34         oos.writeObject(new Date());
    35         Employee emp =new Employee("马云",400);
    36         oos.writeObject(emp);
    37         oos.flush();
    38         byte[] datas =baos.toByteArray();
    39         System.out.println(datas.length);
    40         //读取 -->反序列化
    41         ObjectInputStream ois =new ObjectInputStream(new BufferedInputStream(new ByteArrayInputStream(datas)));
    42         //顺序与写出一致
    43         String msg = ois.readUTF();
    44         int age = ois.readInt();
    45         boolean flag = ois.readBoolean();
    46         char ch = ois.readChar();
    47         System.out.println(flag);
    48         //对象的数据还原
    49         Object str = ois.readObject();
    50         Object date = ois.readObject();
    51         Object employee = ois.readObject();
    52 
    53         if(str instanceof String) {
    54             String strObj = (String) str;
    55             System.out.println(strObj);
    56         }
    57         if(date instanceof Date) {
    58             Date dateObj = (Date) date;
    59             System.out.println(dateObj);
    60         }
    61         if(employee instanceof Employee) {
    62             Employee empObj = (Employee) employee;
    63             System.out.println(empObj.getName()+"-->"+empObj.getSalary());
    64         }
    65 
    66     }
    67 
    68 }
    69 //javabean 封装数据
    70 class Employee implements java.io.Serializable{
    71     private transient String name; //该数据不需要序列化
    72     private double salary;
    73     public Employee() {
    74     }
    75     public Employee(String name, double salary) {
    76         this.name = name;
    77         this.salary = salary;
    78     }
    79     public String getName() {
    80         return name;
    81     }
    82     public void setName(String name) {
    83         this.name = name;
    84     }
    85     public double getSalary() {
    86         return salary;
    87     }
    88     public void setSalary(double salary) {
    89         this.salary = salary;
    90     }
    91 
    92 }

     示例二:

     1 public class SerialDemo {
     2 
     3     public static void main(String[] args) throws IOException, ClassNotFoundException {
     4         //序列化
     5         FileOutputStream fos = new FileOutputStream("object.out");
     6         ObjectOutputStream oos = new ObjectOutputStream(fos);
     7         User user1 = new User("xuliugen", "123456", "male");
     8         oos.writeObject(user1);
     9         oos.flush();
    10         oos.close();
    11         //反序列化
    12         FileInputStream fis = new FileInputStream("object.out");
    13         ObjectInputStream ois = new ObjectInputStream(fis);
    14         User user2 = (User) ois.readObject();
    15         System.out.println(user2.getUserName()+ " " + 
    16             user2.getPassword() + " " + user2.getSex());
    17         //反序列化的输出结果为:xuliugen 123456 male
    18     }
    19 }
    20 
    21 public class User implements Serializable {
    22     private String userName;
    23     private String password;
    24     private String sex;
    25     //全参构造方法、get和set方法省略
    26 }

    object.out文件如下:

     五、注意事项

    1、序列化时,只对对象的状态进行保存,而不管对象的方法;

    2、当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口;

    3、当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化;

    4、并非所有的对象都可以序列化,至于为什么不可以,有很多原因了,比如:

    安全方面的原因,比如一个对象拥有private,public等field,对于一个要传输的对象,比如写到文件,或者进行RMI传输等等,在序列化进行传输的过程中,这个对象的private等域是不受保护的;

    资源分配方面的原因,比如socket,thread类,如果可以序列化,进行传输或者保存,也无法对他们进行重新的资源分配,而且,也是没有必要这样实现;

    5、声明为static和transient类型的成员数据不能被序列化。因为static代表类的状态,transient代表对象的临时数据。

    6、序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联,该序列号在反序列化过程中用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类。为它赋予明确的值。显式地定义serialVersionUID有两种用途:

    在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;

    在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。

    7、Java有很多基础类已经实现了serializable接口,比如String,Vector等。但是也有一些没有实现serializable接口的;

    8、如果一个对象的成员变量是一个对象,那么这个对象的数据成员也会被保存!这是能用序列化解决深拷贝的重要原因;
    参考:https://blog.csdn.net/xlgen157387/article/details/79840134



  • 相关阅读:
    剑指offer系列——56.删除链表中重复的结点
    剑指offer系列——55.链表中环的入口结点
    剑指offer系列——54.字符流中第一个不重复的字符
    剑指offer系列——53.表示数值的字符串
    MinGW与Cygwin
    Android-x86虚拟机安装配置全攻略
    linux下使用NFS挂载文件系统
    ubuntu 64bit “arm-linux-gcc: No such file or directory”问题的解决方法
    虚拟机下ubuntu的minicom使用指南
    Linux 下编译、安装、配置 QT
  • 原文地址:https://www.cnblogs.com/qiaoxin11/p/12602402.html
Copyright © 2011-2022 走看看