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

    为什么要用序列化

    对于一个存在于Java虚拟机中的对象来说,其内部的状态只保持在内存中。JVM停止之后,这些状态就丢失了。在很多情况下,对象的内部状态是需要被持久化下来的。最直接的做法是保存到文件系统或是数据库之中,但这涉及到自定义存储格式以及繁琐的数据转换。序列化提供JVM中对象与字节数组织间的转换,从而实现了对象的持久化。
    序列化分为两大部分:序列化和反序列化。序列化是这个过程的第一部分,将数据分解成字节流,以便存储在文件中或在网络上传输。反序列化就是打开字节流并重构对象。

    什么情况下需要序列化 :

      a)当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;  

      b)当你想用套接字在网络上传送对象的时候;  

      c)当你想通过RMI传输对象的时候; 

     
    序列化的代码实现

    待序列化的Java类只需要实现Serializable接口即可。Serializable仅是一个标记接口,并不包含任何需要实现的具体方法。实现该接口只是为了声明该Java类的对象是可以被序列化的。

    Serializeable接口的序列化和反序列化的实现是通过ObjectOutputStream和ObjectInputStream来完成的。ObjectOutputStream的writeObject方法可以把一个Java对象写入到流中,ObjectInputStream的readObject方法可以从流中读取一个Java对象。Eg: 

    private static void serializePerson() throws FileNotFoundException,IOException {
        try {
            User user = new User("Alex", "Cheng");
            ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("user.bin"));
            output.writeObject(user);
            output.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    private static Person deserializePerson() throws IOException, Exception {
        try {
            ObjectInputStream input = new ObjectInputStream(new FileInputStream("user.bin"));
            User user = (User) input.readObject();
            System.out.println(user);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

     如果想对序列化的过程进行更加细粒度的控制,就需要在类中添加writeObject和对应的 readObject方法。在添加自己的逻辑之前,推荐的做法是先调用Java的默认实现。

    private void writeObject(ObjectOutputStream output) throws IOException {
        output.defaultWriteObject();
        output.writeUTF("Hello World");
    }
    
    private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException {
        input.defaultReadObject();
        String value = input.readUTF();
        System.out.println(value);
    }

     

    什么样的数据能被序列化

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

    - Java对象中的非静态和非瞬时域都会被包括进来,而与域的可见性声明没有关系。声明为static和transient类型的成员数据不能被序列化。这可能会导致某些不应该出现的域被包含在序列化之后 的字节数组中,比如密码等隐私信息。由于Java对象序列化之后的格式是固定的,其它人可以很容易的从中分析出其中的各种信息。对于这种情况,一种解决办法是把域声明为transient。另一种是使用serialPersistentFields声明,即只有firstName这个域是要被序列化的。

    private static final ObjectStreamField[] serialPersistentFields = {
      new ObjectStreamField("firstName", String.class)
    };

    在通过ObjectInputStream的readObject方法读取到一个对象之后,这个对象是一个新的实例,但是其构造方法是没有被调用的,其中的域的初始化代码也没有被执行。对于那些没有被序列化的域,在新创建出来的对象中的值都是默认的。这有可能会造成一些隐含的错误。调用者并不知道对象是通过一般的new操作符来创建的,还是通过反序列化所得到的。解决的办法就是在类的readObject方法里面,再执行所需的对象初始化逻辑。

    Serializable interface doesn't have any method. 

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

    序列化的原理

    通过在实现了Serializable接口的类中定义一个序列号private static final long serialVersionUID = xxxxL; 该序列号在反序列化过程中用于验证序列化对象的,它是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段。对于开发人员来说,需要记得的就是在版本更新过程中保持该值不变。

    如果用户没有自己声明一个serialVersionUID,接口会默认生成一个,但建议自己定义。因为默认的serialVersionUID对于class的细节非常敏感,如果在序列化之后修改了类的定义,那么反序列化得到的serialVersionUID将会和原来不同

    为什么要用RMI技术

    RMI(Remote Method Invocation)是Java中的远程过程调用(Remote Procedure Call,RPC)实现,是一种分布式Java应用的实现方式。它的目的在于对开发人员屏蔽横跨不同JVM和网络连接等细节,使得分布在不同JVM上的对象像是存在于一个统一的JVM中一样,可以很方便的互相通讯。

    RMI的序列化机制

    为了通过Java的序列化机制来进行传输,远程接口中的方法的参数和返回值,要么是Java的基本类型,要么是远程对象,要么是实现了 Serializable接口的Java类。当客户端通过RMI注册表找到一个远程接口的时候,所得到的其实是远程接口的一个动态代理对象。当客户端调用其中的方法的时候,方法的参数对象会在序列化之后,传输到服务器端。服务器端接收到之后,进行反序列化得到参数对象。并使用这些参数对象,在服务器端调用实际的方法。调用的返回值Java对象经过序列化之后,再发送回客户端。客户端再经过反序列化之后得到Java对象,返回给调用者。这中间的序列化过程对于使用者来说是透明的,由动态代理对象自动完成。除了序列化之外,RMI还使用了动态类加载技术。当需要进行反序列化的时候,如果该对象的类定义在当前JVM中没有找到,RMI会尝试从远端下载所需的类文件定义。可以在RMI程序启动的时候,通过JVM参数java.rmi.server.codebase来指定动态下载Java类文件的URL。

    JavaBean的序列化

    Bean的状态信息通常是在设计时配置的。这些状态信息必须保存起来,供程序启动的时候用。对象序列化就负责这个工作。

    Json的序列化

    • JSON.stringify() - 将对象序列化为JSON字符串
    • JSON.parse() - 将JSON数据解析为Javascript对象
  • 相关阅读:
    HDU 2899 Strange fuction
    HDU 2899 Strange fuction
    HDU 2199 Can you solve this equation?
    HDU 2199 Can you solve this equation?
    Java实现 LeetCode 700 二叉搜索树中的搜索(遍历树)
    Java实现 LeetCode 700 二叉搜索树中的搜索(遍历树)
    Java实现 LeetCode 700 二叉搜索树中的搜索(遍历树)
    Java实现 LeetCode 699 掉落的方块(线段树?)
    Java实现 LeetCode 699 掉落的方块(线段树?)
    Java实现 LeetCode 699 掉落的方块(线段树?)
  • 原文地址:https://www.cnblogs.com/qionglouyuyu/p/4615215.html
Copyright © 2011-2022 走看看