zoukankan      html  css  js  c++  java
  • java基础知识回顾之javaIO类--java序列化和反序列化

    /**
     *
     * 一:理解序列化反序列化及其应用
     * 序列化:把堆内存的对象转化成字节流的过程。
     * 反序列化:把字节流序列恢复重构成对象的过程。
     * 对象的序列化的用途:1.把对象的字节序列持久化,保存到硬盘上,通常是文件当中。
     *               2.将对象变成字节流在网络上传输
     * 在很多应用当中,需要对某些对象进行序列化,让他们离开堆内存空间,持久化到硬盘上,以便长期保存。
     * 比如最常见的web服务器中的Session对象,当有10万个用户并发访问,就有可能出现10万个Session对象,
     * 内存吃不消,于是web容器就会把一些Session对象序列化到硬盘上,等要用了在把硬盘中字节序列读到内存中。
     * 而不需要重新创建对象。
     *
     * 再比如,分布式系统在进行网络传输,当两个进程在进行远程通信的时候,在网络上彼此可以发送
     * 各种类型的数据,无论是何种类型的数据都会以二进制的形式在网络上传输。这就用到序列化,将java对象转化成字节序列
     * ,才能在网络上传送,接受方可以把字节序列在恢复为java对象
     *
     * 二、JDK类库中的序列化API
     *
      java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
      java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
      只有实现了Serializable和Externalizable接口的类的对象才能被序列化。Externalizable接口继承自 Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以 采用默认的序列化方式 。
      对象序列化包括如下步骤:
      1) 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;
      2) 通过对象输出流的writeObject()方法写对象。

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

    import java.io.Serializable;
    
    public class Person1 implements Serializable{
    
    
        /**
         * Serializable:用于给被序列化的类加入ID号,用于判断类和对象是否是同一个版本号
         */
        private static final long serialVersionUID = 1L;
        private String name;
        private  int age;
        
        
        public Person1(String name, int age) {
            super();
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
        
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        
    }
    public class ObjectStreamDemo {
    
        /**
         * @param args
         * @throws IOException 
         * @throws ClassNotFoundException 
         */
        public static void main(String[] args) throws IOException, ClassNotFoundException {
                
            writeObj();//对象序列化
            readObj();//反序列化    
        }
        /**
         * 反序列化
         * @throws IOException
         * @throws ClassNotFoundException
         * 对象的反序列化,Person.class文件和obj.object才能实现反序列化
         */
        public static void readObj() throws IOException, ClassNotFoundException {
            
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("lp.object"));
            //对象的反序列化。 
            Person1 p = (Person1)ois.readObject();
            Person1 p1 = (Person1)ois.readObject();
            System.out.println(p.getName()+":"+p.getAge());
            System.out.println(p1.getName()+":"+p1.getAge());
            ois.close();
            
        }
        /**
         * 持久化对象到文件上,如果对象没有实现Serializable接口,就不能被序列化
         * 把堆内存的对象生命周期延长,存放到硬盘上进行持久化,不需要解析,下次我们需要对象,不需要new,直接从硬盘上读重构对象
         * @throws IOException
         * @throws IOException
         */
        public static void writeObj() throws IOException, IOException {
            
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("lp.object"));
            //对象序列化。  被序列化的对象必须实现Serializable接口。 
            oos.writeObject(new Person1("小强",30));
            oos.writeObject(new Person1("lp",40));
            
            oos.close();
        }
    
    }

    输出结果为:小强:30

                      lp:40


    序列化后在项下面生成了lp.object文件,而反序列化是读取lp.object文件重构了对象。

    serialVersionUID的作用:是用于给被序列化的类加入ID号,用于判断类和对象是否是同一个版本号。

    假设我们一开始没有在Person类中没有添加这个版本号,系统默认的序列化运行时会根据类名,接口名,属性名,方法名等等(该类的各个方面)生成一个默认的serialVersionUID序列化版本号来唯一标识此类。加入我们没有加入serialVersionUID,代码如下:

    public class Person1 implements Serializable/*标记接口*/ {
    
        private String name;
        private  int age;
        
        
        public Person1(String name, int age) {
            super();
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
        
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        
    }

    我们调用上面的代码中writeObj()方法,对要序列化的对象进行序列化,然后调用readObj()反序列化,输出结果为:

    小强:30
    lp:40

    好的,我们在对Person类做一个小小的修改,把private int age的名字变为public int age,其它的都不改变。调用readObj()反序列化,发现,程序报错了:

    Exception in thread "main" java.io.InvalidClassException: 无效的类错误

    com.lp.io.p2.bean.Person1; local class incompatible: stream classdesc serialVersionUID = -4619097973205016437, local class serialVersionUID = -7230327405276836859

    出现了两个serialVersionUID 序列号,而且不一样,我们只修改了那么一点,为什么反序列化会报错呢?

    通过看API发现,默认的 serialVersionUID 对类的详细信息具有较高的敏感性,根据编译器的不同可能会千差万别,即使我们在要序列化的类中多加一个空格可能导致serialVersionUID版本号截然不同,编译器会重新生成一个版本号。由于序列版本号是标识序列化类的ID,由于修改前和修改后生成的序列化ID不同,所以java虚拟机认为加载的class有问题,所以报类无效的错误。所以当要改动要序列化的类的时候,最好显示的申明serialVersionUID值,这样当你对这个Person类有小小的改动的时候,只要serialVersionUID值没有改变,就不会抛出以上的异常。

    最后总结:要序列化的类必须实现Serializable接口,只有实现了这个接口才能被序列化,反序列化。在序列化的类中最好显示的申明serialVersionUID值。

     
  • 相关阅读:
    VS远程调试项目,利用msvsmon
    web deploy发布网站各种问题
    IOS中UITabBar、UIDatePicker、Picker View控件练习(二)
    IOS中多视图应用视图切换
    IOS中多视图应用设置首界面
    Asp.net中Application Session Cookie ViewState Cache Hidden
    UI控件(scrollView、tableView)的基本使用
    UI错误集
    UI小项目之拳皇动画的实现(抽取加载图片和播放音乐的方法)
    UI控件(Label、ImageView、Button)的基本使用
  • 原文地址:https://www.cnblogs.com/200911/p/3892737.html
Copyright © 2011-2022 走看看