zoukankan      html  css  js  c++  java
  • Java 序列化与反序列化

    • 序列化与反序列化的概念 

      把对象转换为字节序列的过程称为对象的序列化;将字节序列恢复为对象的过程称为反序列化。

      使用场景:把对象的序列保存到硬盘上,通常放在一个文件中;网络上传送对象的文件序列。

      在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。

    • 序列化接口

      Java对象,实现了Serializable和Externalizable接口就可以实现序列化,Externalizable接口继承自Serializable接口,实现externalizable的对象完全由自身控制序列化的行为,而实现Serializable接口的对象则采用默认的序列化方式。

      java.io.ObjectOutputStream代表对象输出流,可以用writeObject(Object obj)方法将对象序列化之后,将得到的字节序列写到一个目标输出流中;

      java.io.ObjectInputStream代表对象输入流,可以用readObject()从一个源中读取序列,再将其反序列化为一个对象。

    • 序列化与反序列化步骤

      对象序列化包括如下步骤:
      1) 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;
      2) 通过对象输出流的writeObject()方法写对象。

      对象反序列化的步骤如下:
      1) 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;
      2) 通过对象输入流的readObject()方法读取对象。

      首先,创建一个对象:

    import java.io.Serializable;
    
    public class Person implements Serializable {
    
        /**
         * 
         */
        private static final long serialVersionUID = 1L;
        private int age;
        private String name;
        private String sex;
        private int height;
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getSex() {
            return sex;
        }
    
        public void setSex(String sex) {
            this.sex = sex;
        }
    
        public int getHeight() {
            return height;
        }
    
        public void setHeight(int height) {
            this.height = height;
        }
    
        @Override
        public String toString() {
            return "Person [age=" + age + ", name=" + name + ", sex=" + sex + ", height=" + height + "]";
        }
    
    }

      序列化与反序列化:

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    
    import com.changjiang.test.testFuction.entity.Person;
    
    public class TestSerializable {
    
        public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
            serialize();
            deSerializePerson();
        }
    
        private static void serialize() throws FileNotFoundException, IOException {
            Person p = new Person();
            p.setAge(15);
            p.setName("Game");
            p.setHeight(178);
            p.setSex("male");
            ObjectOutputStream ops = new ObjectOutputStream(new FileOutputStream(new File("E:/logs/person.txt")));
            ops.writeObject(p);
            System.out.println("已完成对象Person序列化,并将序列化结果输出到文件");
            ops.close();
        }
    
        private static void deSerializePerson() throws FileNotFoundException, IOException, ClassNotFoundException {
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("E:/logs/person.txt")));
            Person p = (Person) ois.readObject();
            System.out.println("已完成对象Person的反序列化,并将反序列化的结果输出到控制台:" + p);
            ois.close();
        }
    
    }

    最后的输出结果:

    已完成对象Person序列化,并将序列化结果输出到文件
    已完成对象Person的反序列化,并将反序列化的结果输出到控制台:Person [age=15, name=Game, sex=male, height=178]

    而输出文件中的字节内容:

    � sr -com.changjiang.test.testFuction.entity.Person        I ageI heightL namet Ljava/lang/String;L sexq ~ xp      瞭 Gamet male
    • serialVersionUID

      实现Serializable接口的类如果类中没有添加serialVersionUID,那么就会出现如下的警告提示:

    The serializable class Person does not declare a static final serialVersionUID field of type long

    编译器会自动提示以下方案来解决:

    选择前者即是用了默认的方法来生成该ID(1L),而后者则是用类名,接口名,方法和属性等来生成的一个long值。

    它究竟有什么作用呢?

    在之前的例子中,我们在Person类中加入了一句:

        private static final long serialVersionUID = 1L;

    并且在TestSerializable这个类中将Person对象序列化进了一个文本文件,如果此时我们修改了这个ID的值,那么反序列化就会得到异常信息:

    Exception in thread "main" java.io.InvalidClassException: com.changjiang.test.testFuction.entity.Person; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
        at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:617)
        at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1622)
        at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1517)
        at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1771)
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350)
        at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
        at com.changjiang.test.testFuction.test.TestSerializable.deSerializePerson(TestSerializable.java:34)
        at com.changjiang.test.testFuction.test.TestSerializable.main(TestSerializable.java:17)

    这就是说,序列化的结果是根据ID=1L计算得出的,当修改了这个值之后,文件流中的class和classpath中的class,也就是修改过后的class,不兼容了,处于安全机制考虑,程序抛出了错误,并且拒绝载入。所以,想要修改已经序列化的类之后再反序列化之前的内容,只要前后的ID一致即可。

      serialVersionUID的取值是Java运行时环境根据类的内部细节自动生成的。如果对类的源代码作了修改,再重新编译,新生成的类文件的serialVersionUID的取值有可能也会发生变化。如果不对该值显式定义出来,类的serialVersionUID的默认值则完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,有可能会导致不同的 serialVersionUID,也有可能相同。所以,简单地说,希望兼容,则显示定义一致的ID,希望不兼容,则显示定义不一致的ID,为了提高serialVersionUID的独立性和确定性,强烈建议在一个可序列化的类中对其赋予明确的值。

  • 相关阅读:
    模式识别之svm()---支持向量机svm 简介1995
    机器视觉之相关资源--- 计算机视觉相关的部分测试数据集和源码站点
    包的引入,不要引入自己目录下的。
    内省对象 用的少,被BeanUtils代替
    使用增强for循环遍历集合的时候操作集合的问题?
    Integer 内部实现
    eclipse常用快捷键
    java 运行时环境和编译器环境
    js 随机变换图片
    js 事件点击 显示 隐藏
  • 原文地址:https://www.cnblogs.com/bruceChan0018/p/5845414.html
Copyright © 2011-2022 走看看