Java中的序列化和反序列化:深入理解和实战

2024-01-09 02:52:47

1. 简介

序列化和反序列化的本质是解决在进行远程通信和持久化数据时,如何保存和恢复数据的问题。

  • ** 序列化:** 将数据结构或对象转换成二进制字节流的过程
  • ** 反序列化: ** 将在序列化过程中所生成的二进制字节流转换成数据结构或者对象的过程

为什么要进行序列化和反序列化操作?
其实主要是为了持久化和进行传输,我们都知道在Java 中一些数据都是以对象的形式存在的,那如果我想把对象传输其他人怎么办?

那肯定是不能直接传输的,假如我们要进行网络传输,传输的过程中是不能传输Java 对象的,因为底层的实现并不知道Java 对象的存在,我们一般都是传输字节,所以我们需要能够进行传输的形式,序列化就是这个操作,我们将Java 对象序列化为一个字节数组,这样就可以进行传输了。

这个原理不仅在Java中,在所有需要进行远程通信和数据存储的编程语言中都存在。

2. Java中的序列化

为了在Java中实现序列化,我们需要完成以下两个步骤。

首先,让我们要序列化的类实现Serializable接口。下面是一个实现了Serializable接口的Person类。

public class Person implements Serializable {
    private String name;
    // getter 和 setter 方法省略
}

然后,我们可以使用java.io.ObjectOutputStream类将Person对象写入到硬盘中,如下所示:

Person person = new Person();
person.setName("Grace");

try (FileOutputStream fos = new FileOutputStream("person.obj");
     ObjectOutputStream oos = new ObjectOutputStream(fos)) {
    oos.writeObject(person);
} catch (IOException e) {
    e.printStackTrace();
}

3. Java中的反序列化

反序列化就是将序列化的字节流恢复成原来的对象。我们可以通过java.io.ObjectInputStream类来完成这个过程,如下所示:

try (FileInputStream fis = new FileInputStream("person.obj");
     ObjectInputStream ois = new ObjectInputStream(fis)) {
    Person person = (Person) ois.readObject();
    System.out.println(person.getName());
} catch (IOException | ClassNotFoundException e) {
    e.printStackTrace();
}

4. Java序列化中的常见问题和解决策略

序列化并不总是那么简单。我们可能会遇到版本控制问题、子类序列化,以及某些字段不应该被序列化的问题。

  • 版本控制问题: 在序列化对象中,serialVersionUID是JAVA为了有效地实现序列化所引入的。serialVersionUID作为版本控制,在反序列化过程中,JAVA虚拟机会根据版本号确保序列化类的一致性。如果对象的版本号不一致,反序列化过程就会失败,抛出InvalidClassException异常。示例:

    public class Person implements Serializable {
        private static final long serialVersionUID = 1L;
        // 其他代码省略
    }
    
  • 子类序列化: 在Java中,父类通过实现Serializable接口,子类可以由此自动实现序列化,反之则不行。当子类需要序列化,而父类并未实现序列化接口时,可以通过在子类添加一个父类的构造器来解决。这样的示例:

    public class Person {
        private String name;
        Person(String name){
            this.name = name;
            // 其他代码省略
        }
    }
    
    public class Employee extends Person implements Serializable {
        private int employeeId;
        Employee(String name, int employeeId) {
            super(name);
            this.employeeId = employeeId;
            // 其他代码省略
        }
    }
    
  • transient关键字: 在序列化过程中,可能某些字段并不需要序列化,比如说一些敏感信息(如密码、银行卡号等)。这时可以使用transient关键字标记不需要序列化的字段。值得注意的是,被transient关键字标记的字段,在反序列化回来后将为null。示例:

    public class Person implements Serializable {
        private String name;
        private transient String password; // 此字段不会被序列化
        // getter 和 setter 方法省略
    }
    

5. 自定义序列化

有时候,我们可能想要更多地控制序列化的过程。Java为我们提供了自定义序列化的方法——通过在我们的类中重写writeObject(ObjectOutputStream out)和readObject(ObjectInputStream in)方法,我们就可以控制对象的序列化过程。示例:

public class Person implements Serializable {
    private String name;
    private transient String password; // 此字段不会被序列化

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        out.writeObject(password); 
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        password = (String) in.readObject(); 
    }
  
    // getter 和 setter 方法省略
}

6. 常见问题及解答

  • 没有实现Serializable接口的类的对象能否序列化? 不可以。如果对象的类没有实现 Serializable 接口,那么将无法通过ObjectOutputStream进行序列化。
  • 静态变量参与序列化吗? 不参与。因为static的变量属于类级别的,序列化只处理对象的状态,即类的非静态字段。静态变量的生命周期和对象不同,由JVM负责维护。
  • 如何控制是否序列化字段? 对于不需要序列化的字段,我们可以使用transient关键字。被transient修饰的字段在序列化时将被忽略,反序列化时按照字段类型的默认值填充。
  • 如何保证序列化对象的安全性? 对于需要序列化的敏感信息对象,我们可以在序列化时加密,反序列化时解密来保证安全性。另外,选择可靠的存储和传输方式也是重要的安全保障。

7. 使用场景

序列化被广泛应用于各种场景,如远程调用(RMI或EJB),持久化(Hibernate),HTTP会话在集群节点间的复制等。理解序列化及反序列化的原理和实现,对掌握Java及各种Java框架和中间件至关重要。

下面是序列化和反序列化常见应用场景:

  • 对象在进行网络传输(比如远程方法调用 RPC 的时候)之前需要先被序列化,接收到序列化的对象之后需要再进行反序列化;
  • 将对象存储到文件之前需要进行序列化,将对象从文件中读取出来需要进行反序列化;
  • 将对象存储到数据库(如 Redis)之前需要用到序列化,将对象从缓存数据库中读取出来需要反序列化;
  • 将对象存储到内存之前需要进行序列化,从内存中读取出来之后需要进行反序列化。

8. 结尾

序列化和反序列化看似简单的概念,但实则包含了许多复杂的细节。希望通过这篇文章,能够帮助大家更深入地理解序列化和反序列化的过程,以及如何在实际编程中使用序列化。

如文章有任何疑问,欢迎提出!
欢迎大家访问我的个人博客 无限进步的博客

文章来源:https://blog.csdn.net/qq_23373299/article/details/135387976
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。