什么是序列化? 您需要通过示例解释有关Java序列化的所有知识

In a previous article, we looked at 5 different ways to create objects in java, I have explained how deserialising a serialised object creates a new object and in this blog, I am going to discuss Serialization and Deserialization in details.

我们将在下面使用雇员类对象为例进行说明

// If we use Serializable interface, static and transient variables do not get serialize
class Employee implements Serializable {

    // This serialVersionUID field is necessary for Serializable as well as Externalizable to provide version control,
    // Compiler will provide this field if we do not provide it which might change if we modify the class structure of our class, and we will get InvalidClassException,
    // If we provide value to this field and do not change it, serialization-deserialization will not fail if we change our class structure.
    private static final long serialVersionUID = 2L;

    private final String firstName; // Serialization process do not invoke the constructor but it can assign values to final fields
    private transient String middleName; // transient variables will not be serialized, serialised object holds null
    private String lastName;
    private int age;
    private static String department; // static variables will not be serialized, serialised object holds null

    public Employee(String firstName, String middleName, String lastName, int age, String department) {
        this.firstName = firstName;
        this.middleName = middleName;
        this.lastName = lastName;
        this.age = age;
        Employee.department = department;

        validateAge();
    }

    private void validateAge() {
        System.out.println("Validating age.");

        if (age < 18 || age > 70) {
            throw new IllegalArgumentException("Not a valid age to create an employee");
        }
    }

    @Override
    public String toString() {
        return String.format("Employee {firstName='%s', middleName='%s', lastName='%s', age='%s', department='%s'}", firstName, middleName, lastName, age, department);
    }

  // Custom serialization logic,
    // This will allow us to have additional serialization logic on top of the default one e.g. encrypting object before serialization
    private void writeObject(ObjectOutputStream oos) throws IOException {
        System.out.println("Custom serialization logic invoked.");
        oos.defaultWriteObject(); // Calling the default serialization logic
    }

    // Custom deserialization logic
    // This will allow us to have additional deserialization logic on top of the default one e.g. decrypting object after deserialization
    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        System.out.println("Custom deserialization logic invoked.");

        ois.defaultReadObject(); // Calling the default deserialization logic

        // Age validation is just an example but there might some scenario where we might need to write some custom deserialization logic
        validateAge();
    }

}

What are Serialization and Deserialization

在Java中,我们创建了一些对象,这些对象会相应地存活和死亡,并且当JVM死亡时,每个对象肯定会死亡,但是有时我们可能想在多个JVM之间重用一个对象,或者可能希望通过网络将对象传输到另一台机器。

好,序列化允许我们将对象的状态转换为字节流,然后可以将其保存到本地磁盘上的文件中,或者通过网络发送到任何其他计算机。 和de序列化使我们可以逆转该过程,这意味着将序列化的字节流再次转换为对象。

简单来说,对象序列化是将对象状态保存为字节序列的过程,de序列化是从这些字节重建对象的过程。 通常,整个过程称为序列化但我认为最好将两者分别分类以提高清晰度。

序列化过程与平台无关,可以在一个平台上反序列化在一个平台上序列化的对象。

要将对象序列化和反序列化为文件,我们需要调用ObjectOutputStream.writeObject()和ObjectInputStream.readObject()如以下代码所示:

public class SerializationExample {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Employee empObj = new Employee("Shanti", "Prasad", "Sharma", 25, "IT");
        System.out.println("Object before serialization  => " + empObj.toString());

        // Serialization
        serialize(empObj);

        // Deserialization
        Employee deserialisedEmpObj = deserialize();
        System.out.println("Object after deserialization => " + deserialisedEmpObj.toString());
    }

    // Serialization code
    static void serialize(Employee empObj) throws IOException {
        try (FileOutputStream fos = new FileOutputStream("data.obj");
             ObjectOutputStream oos = new ObjectOutputStream(fos))
        {
            oos.writeObject(empObj);
        }
    }

    // Deserialization code
    static Employee deserialize() throws IOException, ClassNotFoundException {
        try (FileInputStream fis = new FileInputStream("data.obj");
             ObjectInputStream ois = new ObjectInputStream(fis))
        {
            return (Employee) ois.readObject();
        }
    }
}

Only classes which implement Serializable can be serialized

Similar to the Cloneable interface for Java cloning in serialization, we have one marker interface Serializable which works like a flag for the JVM. Any class which implements 小号erializable interface directly or through its parent can be serialised and classes which do not implement 小号erializable can not be serialized.

Java的默认序列化过程是完全递归的,因此,每当我们尝试序列化一个对象时,序列化过程都会尝试将所有字段(原始和引用)与我们的类序列化(除了静态的和短暂的字段)。

当一个类实现可序列化接口,其所有子类也可序列化。 但是,当一个对象引用另一个对象时,这些对象必须实现可序列化界面分开。 如果我们的班级甚至对非可序列化类,然后JVM将抛出Not可序列化Exception。

Why Serializable is not implemented by Object?

现在出现一个问题,即序列化是否是非常基本的功能以及任何未实现的类可序列化 can not be serialised, then why 可序列化 is not implemented by the 宾语本身?,通过这种方式,我们所有的对象都可以默认情况下被序列化。

的宾语类未实现可序列化接口,因为我们可能不想序列化所有对象,例如 序列化线程没有任何意义,因为在JVM中运行的线程将占用系统内存,将其持久化并尝试在JVM中运行该线程是没有意义的。

The transient and static fields do not get serialized

如果我们要序列化一个对象但不想序列化某些特定字段,则可以将这些字段标记为短暂的。

一切静态的 fields belong to the class instead of the object, and the serialization process serialises the object so 静态的 fields can not be serialized.

序列化并不关心字段的访问修饰符,例如私人的。 所有非瞬态和非静态字段都被视为对象持久状态的一部分,并且可以序列化。我们只能在征服者中将值分配给final字段,并且序列化过程不会调用任何构造函数,但仍可以将值分配给final字段。

What is serialVersionUID and Why should we declare it?

假设我们有一个类,并且已将其对象序列化到磁盘上的文件中,并且由于一些新要求,我们在类中添加/删除了一个字段。 现在,如果我们尝试反序列化已经序列化的对象,我们将得到InvalidClassException,为什么呢?

我们之所以得到它,是因为默认情况下,JVM将版本号与每个可序列化的类相关联,以控制类的版本控制。 它用于验证序列化和反序列化的对象具有相同的属性,从而与反序列化兼容。 版本号保存在一个名为serialVersionUID。 如果可序列化的类未声明serialVersionUIDJVM将在运行时自动生成一个。

如果我们改变班级结构,例如 删除/添加版本号也会更改的字段,根据JVM,我们的类与序列化对象的类版本不兼容。 这就是为什么我们得到例外,但是如果您真的考虑过它,为什么应该仅仅因为我添加了一个字段就抛出该例外? 不能仅将字段设置为其默认值,然后下次将其写出吗?

是的,可以通过提供serialVersionUID手动设置字段,并确保它始终相同。 强烈建议每个可序列化的类声明其serialVersionUID因为生成的代码依赖于编译器,因此可能导致意外InvalidClassExceptions。

您可以使用JDK发行版随附的实用程序,称为串行器查看默认情况下的代码(默认情况下只是对象的哈希代码)。

Customizing Serialization and Deserialization with writeObject and readObject methods

JVM可以完全控制默认序列化过程中的对象序列化,但是使用默认序列化过程有很多缺点,其中包括:

  1. 它无法处理无法序列化的字段的序列化。反序列化过程在创建对象时不会调用构造函数,因此它无法调用构造函数提供的初始化逻辑。

但是我们可以在Java类中覆盖此默认的序列化行为,并提供一些其他逻辑来增强正常过程。 这可以通过提供两种方法来完成writeObject和readObject在我们要序列化的类中:

// Custom serialization logic will allow us to have additional serialization logic on top of the default one e.g. encrypting object before serialization
private void writeObject(ObjectOutputStream oos) throws IOException {
  // Any Custom logic
 oos.defaultWriteObject(); // Calling the default serialization logic
  // Any Custom logic
}

// Custom deserialization logic will allow us to have additional deserialization logic on top of the default one e.g. decrypting object after deserialization
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
 // Any Custom logic
 ois.defaultReadObject(); // Calling the default deserialization logic
  // Any Custom logic
}

将两个方法都声明为私有是必要的(公共方法将不起作用),因此除了JVM之外,其他任何东西都看不到它们。 这也证明方法既不会继承,也不会被覆盖或重载。 JVM自动检查这些方法并在序列化/反序列化过程中调用它们。 JVM可以调用这些私有方法,但其他对象则不能,因此,该类的完整性得到维护,序列化协议可以继续正常工作。

即使提供了那些专用的私有方法,对象序列化也可以通过调用ObjectOutputStream.writeObject()要么ObjectInputStream.readObject()。

致电ObjectOutputStream.writeObject()要么ObjectInputStream.readObject()开始序列化协议。 首先,检查对象以确保其实现可序列化然后检查是否提供了这些私有方法中的任何一个。 如果提供了它们,则将流类作为参数传递给这些方法,从而使代码可以控制其用法。

我们可以打电话ObjectOutputStream.defaultWriteObject()和ObjectInputStream.defaultReadObject() from these methods to gain default serialization logic. Those calls do what they sound like -- they perform the default writing和reading of the serialized object, which is important because we are not replacing the normal process, we are only adding to it.

这些私有方法可用于您要在序列化过程中进行的任何自定义,例如 加密可以添加到输出中,解密可以添加到输入中(请注意,字节以明文形式写入和读取,完全没有混淆)。 它们可能被用来向流中添加额外的数据,也许是公司的版本代码,可能性是无限的。

Apart from writeObject and readObject methods, we can also leverage other serialization magic methods to get some additional functionality.

Stopping Serialization and Deserialization

假设我们有一个从其父级获得序列化功能的类,这意味着我们的类是从另一个实现该类的类扩展的可序列化。

It means anybody can serialize and deserialize the object of our class. But what if we do not want our class to be serialized or deserialized e.g. our class is a singleton and we want to prevent any new object creation, remember the deserialization process creates a new object.

要停止对我们的类进行序列化,我们可以再次使用上述私有方法来抛出NotSerializableException。 现在,任何对我们的对象进行序列化或反序列化的尝试都将始终导致引发异常。 并且由于这些方法被声明为私人的,没有人可以覆盖您的方法并进行更改。

private void writeObject(ObjectOutputStream oos) throws IOException {
  throw new NotSerializableException("Serialization is not supported on this object!");
}

private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
  throw new NotSerializableException("Serialization is not supported on this object!");
}

However, this is a violation of the Liskov Substitution Principle. And writeReplace and readResolve methods can be used to achieve singleton like behaviours. These methods are used to allow an object to provide an alternative representation for itself within an ObjectStream. In simple words, readResolve can be used to change the data that is deserialized through the readObject method and writeReplace can be used to change the data that is serialized through writeObject.

Java serialization can also be used to deep clone an object. Java cloning is the most debatable topic in Java community and it surely does have its drawbacks but it is still the most popular and easy way of creating a copy of an object until that object is full filling mandatory conditions of Java cloning. I have covered cloning in details in a 3 article long Ĵava Cloning Series which includes articles like Ĵava Cloning And Types Of Cloning (Shallow And Deep) In Details With Example, Ĵava Cloning - Copy Constructor Versus Cloning, Ĵava Cloning - Even Copy Constructors Are Not Sufficient, go ahead and read them if you want to know more about cloning.

Conclusion

  1. 序列化istheprocessofsavinganobject'sstatetoasequenceofbyteswhichthencanbestoredonafileorsentoverthenetworkand反序列化istheprocessofreconstructinganobjectfromthosebytes。Onlysubclassesofthe可序列化interfacecanbeserialized。Ifourclassdoesnotimplement可序列化interfaceorifitishavingareferencetoanon可序列化classthenJVMwillthrowNot可序列化Exception。All短暂的and静态的fieldsdonotgetserialized。TheserialVersionUIDisusedtoverifythattheserializedanddeserializedobjectshavethesameattributesandthusarecompatiblewith反序列化。WeshouldcreateaserialVersionUIDfieldinourclasssoifwechangeourclassstructure(adding/removingfields)JVMwillnotthroughInvalidClassException。IfwedonotprovideitJVMprovidesonewhichmightchangewhenourclassstructurechanges。WecanoverridethedefaultserializationbehaviourinsideourJavaclassbyprovidingtheimplementationofwriteObjectandreadObjectmethods。AndwecancallObjectOutputStream。defaultWriteObject()andObjectInputStream。defaultReadObjectfromwriteObjectandreadObjectmethodstogetthedefaultserializationand反序列化logic。WecanthrowNot可序列化ExceptionexceptionfromwriteObjectandreadObject,ifwedonotwantourclasstobeserializedordeserialized。

Java Serialization process can be further customized and enhanced using the Externalizable interface which I have explained in How to Customize Serialization In Ĵava By Using Externalizable Interface.

我还写了一系列文章,解释了有效Java的项目编号74到78,它进一步讨论了如何增强Java序列化过程,请继续阅读并阅读。

You can find the complete source code for this article on this Github Repository and please feel free to provide your valuable feedback.

from: https://dev.to//njnareshjoshi/what-is-serialization-everything-you-need-to-know-about-java-serialization-explained-with-example-9mj

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java序列化是指将一个对象转换成一个字节流,以便可以将其存储在文件中、通过网络传输或在进程之间传递。序列化可以用来保存对象的状态,以便可以在以后的时间重建该对象,而无需重新创建它。需要序列化的情况包括:网络传输,对象持久化,进程之间通信等。 以下是Java序列化代码示例: ```java import java.io.*; public class SerializationDemo { public static void main(String[] args) { // Serialize object Student student = new Student("Tom", "M", 23); try { FileOutputStream fileOut = new FileOutputStream("student.ser"); ObjectOutputStream out = new ObjectOutputStream(fileOut); out.writeObject(student); out.close(); fileOut.close(); System.out.println("Serialized data is saved in student.ser file"); } catch (IOException i) { i.printStackTrace(); } // Deserialize object try { FileInputStream fileIn = new FileInputStream("student.ser"); ObjectInputStream in = new ObjectInputStream(fileIn); Student deserializedStudent = (Student) in.readObject(); in.close(); fileIn.close(); System.out.println("Deserialized data:"); System.out.println("Name: " + deserializedStudent.getName()); System.out.println("Gender: " + deserializedStudent.getGender()); System.out.println("Age: " + deserializedStudent.getAge()); } catch (IOException i) { i.printStackTrace(); } catch (ClassNotFoundException c) { System.out.println("Student class not found"); c.printStackTrace(); } } } class Student implements Serializable { private String name; private String gender; private int age; public Student(String name, String gender, int age) { this.name = name; this.gender = gender; this.age = age; } public String getName() { return name; } public String getGender() { return gender; } public int getAge() { return age; } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值