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

    什么是序列化和反序列化?

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

    总之,序列化和反序列化操作的是java对象(Object)

    序列化:把java对象转换成二进制码

    反序列化:把java对象的二进制码转换回java对象

    对象的序列化主要有两种用途:
      1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
      2) 在网络上传送对象的字节序列。

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

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

    如何实现序列化和反序列化?

    1.让想要可序列化的对象实现空接口Serializable,意思是表示该类的对象可序列化.

    2.使用java.io包内的序列化API来实现序列化和反序列化.

    可序列化API

    java.io.ObjectOutputStream :

      public ObjectOutputStream(OutputStream out) : 构造函数,以OutputStream对象作为参数

      public final void writeObject(Object obj) : 把对象转换成二进制码并写入到本地文件

    java.io.ObjectInputStream :

      public ObjectInputStream(InputStream in) : 构造函数,以InputStream对象作为参数

      public final Object readObject() : 读取本地文件中保存的二进制码,并转换为Object对象

    实例

    Person类:

    public class Person implements Serializable {
       //序列化的版本号,下边将会介绍
       private static final long serialVersionUID = 4744551959239088922L;
       private int id;
        private String name;
        private String sex;
        private int age;
        private String desc;
    
        public Person(int id, String name, String sex, int age, String desc) {
            this.id = id;
            this.name = name;
            this.sex = sex;
            this.age = age;
            this.desc = desc;
        }
    
    	@Override
    	public String toString() {
    		return "Person [id=" + id + ", name=" + name + ", sex=" + sex + ", age=" + age + ", desc=" + desc + "]";
    	}
    }
    

      

    序列化和反序列化测试类:

    public class TestObjSerializeAndDeserialize {
    	File File = new File("C:/test/person.txt");
    
    	/**
    	 * 序列化,把Person对象序列化并存储到本地文件中(C:/test/person.txt)
    	 */
    	@Test
        public  void serializePerson() {
            Person p = new Person(1,"张三", "男", 18, "废人一个");
            try {
                ObjectOutputStream objOutStream = new ObjectOutputStream(new FileOutputStream(File));
                objOutStream.writeObject(p);
                System.out.println("对象序列化成功");
                objOutStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    	/**
    	 * 反序列化,读取本地文件的二进制内容,反序列化为Person对象
    	 */
    	@Test
    	public void deserialize(){
    		try {
    			ObjectInputStream objInStream = new ObjectInputStream(new FileInputStream(File));
    			Person person = (Person)objInStream.readObject();
    			System.out.println(person);
    			objInStream.close();
    		} catch (FileNotFoundException e) {
    			e.printStackTrace();
    		} catch (IOException e) {
    			e.printStackTrace();
    		} catch (ClassNotFoundException e) {
    			e.printStackTrace();
    		}
    	}
    
    
    }

    什么是serialVersionUID?

      凡是实现了Serializable接口的类,都会有一个s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D静态常量

      如果没有手动设定该变量,那么IDE会报警告,如下图

      

      点击Add generated serial version ID即可根据类文件自动生成s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D变量.

    为什么要显式地设置s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D?

    1.避免修改对象的类内容导致的s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D值不一致 :

      如果没有显式地声明s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D静态常量,那么Java编译器会自动给该类生成一个唯一的s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D,只要类的内容稍有不同,

      该类的s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D值就会完全不同.由此我们可以推断出 :

        如果把对象序列化存储到本地后,我们修改了对象的类的内容,那么该类的s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D也会不同了,

        这时我们反序列化该类,调用readObject()方法时,就会报异常:java.io.InvalidClassException,内容为前后serialVersionUID值不匹配

      如果我们显式地声明了s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D静态常量;就可以避免该异常,即使修改类的内容,也可以正常进行反序列化.

    2.避免相同类文件用不同java编译器导致的s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D值不一致 : 

      类的serialVersionUID的默认值完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,

      有可能会导致不同的 serialVersionUID,也有可能相同。为了提高serialVersionUID的独立性和确定性,

      强烈建议在一个可序列化类中显示的定义serialVersionUID,为它赋予明确的值

    序列化并存储到本地文件后,打开文件的乱码问题 :

    正常.序列化后,输出的完全是二进制内容,所以是乱码

  • 相关阅读:
    【javascript基础】JS计算字符串所占字节数
    mysql设置有外键的主键自增及其他
    spring AOP简单实现代码存放
    Dockerfile指令及docker的常用命令
    ubuntu 16.04 jenkins pipline的实现 最终docker启动服务
    ubuntu16.04 docker安装
    ubuntu16.04 nginx安装
    ubuntu14.04 spring cloud config server + gradle搭建
    ubuntu14.04 python2.7安装MySQLdb
    flask初次搭建rest服务笔记
  • 原文地址:https://www.cnblogs.com/jinyu59/p/10780410.html
Copyright © 2011-2022 走看看