• java类和对象:类的序列化


    1、序列化介绍

    Java 串行化技术可以使你将一个对象的状态写入一个Byte流里,并且可以从其它地方把该Byte 流里的数据读出来,重新构造一个相同的对象。这种机制允许你将对象通过网络进行传播,并可以随时把对象持久化到数据库、文件等系统里。Java的串行化机制是RMI、EJB等技术的技术基础。

    用途:利用对象的串行化实现保存应用程序的当前工作状态,下次再启动的时候将自动地恢复到上次执行的状态。

    序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。

    序列化是为了解决在对对象流进行读写操作时所引发的问题。

    2、串行化的特点

      (1)如果某个类能够被串行化,其子类也可以被串行化。如果该类有父类,则分两种情况来考虑,如果该父类已经实现了可串行化接口。则其父类的相应字段及属性的处理和该类相同;如果该类的父类没有实现可串行化接口,则该类的父类所有的字段属性将不会串行化。

      (2)声明为static和transient类型的成员数据不能被串行化。因为static代表类的状态, transient代表对象的临时数据;

      (3)相关的类和接口:在java.io包中提供的涉及对象的串行化的类与接口有ObjectOutput接口、ObjectOutputStream类、ObjectInput接口、ObjectInputStream类。

    • ObjectOutput接口:它继承DataOutput接口并且支持对象的串行化,其内的writeObject()方法实现存储一个对象。
    • ObjectInput接口:它继承DataInput接口并且支持对象的串行化,其内的readObject()方法实现读取一个对象。
    • ObjectOutputStream类:它继承OutputStream类并且实现ObjectOutput接口。利用该类来实现将对象存储(调用ObjectOutput接口中的writeObject()方法)。
    • ObjectInputStream类:它继承InputStream类并且实现ObjectInput接口。利用该类来实现读取一个对象(调用ObjectInput接口中的readObject()方法)。

       对于父类的处理,如果父类没有实现串行化接口,则其必须有默认的构造函数(即没有参数的构造函数)。否则编译的时候就会报错。在反串行化的时候,默认构造函数会被调用。但是若把父类标记为可以串行化,则在反串行化的时候,其默认构造函数不会被调用。这是因为Java 对串行化的对象进行反串行化的时候,直接从流里获取其对象数据来生成一个对象实例,而不是通过其构造函数来完成。

    3、序列化的实现

    基本方法:将需要被序列化的类实现Serializable接口,然后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,接着,使用ObjectOutputStream对象的writeObject(Object obj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流。

    serialVersionUID的作用:简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException)

    serialVersionUID有两种显示的生成方式:       

    • 一个是默认的1L,比如:private static final long serialVersionUID = 1L;       
    • 一个是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段,比如:private static final long serialVersionUID = xxxxL;

    4、类的序列化读写实现

    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 java.io.Serializable;
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    import junit.framework.TestCase;
    /**
     * 使用Java序列化把对象存储到文件中,再从文件中读出来 注意读取的时候,读取数据的顺序一定要和存放数据的顺序保持一致
     * 
     * @author Champion Wong
     * 
     */
    public class Test08 extends TestCase {
        public void test() {
            // 创建一个User对象
            User user = new User();
            user.setId(1);
            user.setName("Mr XP.Wang");
            // 创建一个List对象
            List<String> list = new ArrayList<String>();
            list.add("My name");
            list.add(" is");
            list.add(" Mr XP.Wang");
            try {
                ObjectOutputStream os = new ObjectOutputStream(
                        new FileOutputStream("C:/wxp.txt"));
                os.writeObject(user);// 将User对象写进文件
                os.writeObject(list);// 将List列表写进文件
                os.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                ObjectInputStream is = new ObjectInputStream(new FileInputStream(
                        "C:/wxp.txt"));
                User temp = (User) is.readObject();// 从流中读取User的数据
                System.out.println(temp.getId());
                System.out.println(temp.getName());
                List tempList = (List) is.readObject();// 从流中读取List的数据
                for (Iterator iterator = tempList.iterator(); iterator.hasNext();) {
                    System.out.print(iterator.next());
                }
                is.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    class User implements Serializable {
        private int id;
        private String name;
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    }

     5、类的序列化应用探讨

    用途利用对象的串行化实现保存应用程序的当前工作状态,下次再启动的时候将自动地恢复到上次执行的状态。例如:游戏状态的保存。

    如何保持向上兼容性

    向上兼容性是指老的版本能够读取新的版本序列化的数据流。常常出现在我们的服务器的数据更新了,仍然希望老的客户端能够支持反序列化新的数据流,直到其更新到新的版本。可以说,这是半自动的事情。

    跟一般的讲,因为在java中serialVersionUID是唯一控制着能否反序列化成功的标志,只要这个值不一样,就无法反序列化成功。但只要这个值相同,无论如何都将反序列化,在这个过程中,对于向上兼容性,新数据流中的多余的内容将会被忽略;对于向下兼容性而言,旧的数据流中所包含的所有内容都将会被恢复,新版本的类中没有涉及到的部分将保持默认值。利用这一特性,可以说,只要我们认为的保持serialVersionUID不变,向上兼容性是自动实现的。

    当然,一但我们将新版本中的老的内容拿掉,情况就不同了,即使UID保持不变,会引发异常。正是因为这一点,我们要牢记一个类一旦实现了序列化又要保持向上下兼容性,就不可以随随便便的修改了!

    如何保持向下兼容性

    一如上文所指出的,你会想当然的认为只要保持serialVersionUID不变,向下兼容性是自动实现的。但实际上,向下兼容要复杂一些。这是因为,我们必须要对那些没有初始化的字段负责。要保证它们能被使用。

    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException{ 
     in.defaultReadObject();//先反序列化对象 
     if(ver=5552){
      //以前的版本5552 
      …初始化其他字段 
     }else if(ver=5550){
      //以前的版本5550 
      …初始化其他字段 
     }else{
      //太老的版本不支持 
      throw new InvalidClassException(); 
     } 
    }

    细心的读者会注意到要保证in.defaultReadObject();能够顺利执行,就必须要求serialVersionUID保持一致,所以这里的ver不能够利用serialVersionUID了。这里的ver是一个我们预先安插好的final long ver=xxxx;并且它不能够被transient修饰。

    所以保持向下的兼容性至少有三点要求:

    1.serialVersionUID保持一致

    2.预先安插好我们自己的版本识别标志的final long ver=xxxx;

    3.保证初始化所有的域

  • 相关阅读:
    实现一个圆形进度条
    给你的jQuery项目赋予Router技能吧
    matlab新手入门(四)(翻译)
    matlab新手入门(三)(翻译)
    matlab新手入门(二)(翻译)
    matlab新手入门(一)(翻译)
    安装Matlab出现Error 1935错误解决方法
    Linux Ubuntu下Jupyter Notebook的安装
    lung 分割论文
    链表的回文结构
  • 原文地址:https://www.cnblogs.com/xiaotian15/p/3665018.html
Copyright © 2020-2023  润新知