作者Madhoriya22:序列化

介绍

听到“串行化”一词时,想到的第一个问题是...

“什么是序列化?” 我们知道我们可以用Java创建可重用的对象。 但是,这些对象的生存期仅在Java虚拟机运行时才持续。 一旦关闭JVM,我们将丢失所有这些对象。

如果有人希望在JVM重新启动后可以使用对象,该怎么办? 这就是序列化的作用。

序列化是将对象的当前状态存储到流中的过程。 该流充当对象的容器。 稍后,我们可以从该流中还原等效对象。

序列化或外部化

流容器可以是以下两种类型之一:

  • 瞬态(基于RAM)或
  • 持久(基于磁盘)。

瞬态存储用于创建对象以从一台计算机传输到另一台计算机。

持久存储允许在当前会话结束后使用对象。

有两种方法可以序列化类对象:

(i)通过实现Serializable接口:


public class MyClass implements Serializable {
........
........//your code goes here
} 
(ii)通过实现Externalizable接口:

public class MyClass implements Externalizable {
........
........//your code goes here
} 
接口

现在,有关Serializable接口的一些重要事实:

  • Serializable接口本身没有任何方法。 相反,当一个类实现Serializable时,它告诉JVM可以使用持久流容器存储该类。
  • 此接口依靠Java的运行时默认机制来保存对象的状态。
  • 通过类ObjectOutputStream的writeObject()方法(或在objectOutput接口中)将对象写入流中。
  • 为了写入原始值,我们可以使用write <datatype>()方法。
  • 读取对象是通过ObjectInputStream类的readObject()方法完成的。
  • 序列化类的所有子类本身都已序列化。

有关Externalizable接口的一些重要事实:

  • 与Serializable接口不同,Externalizable接口指定实现类将自行处理序列化,而不是依赖默认的运行时机制。
  • 请注意,这意味着您有责任确保以正确的顺序保存所有数据。

Externalizable声明两个方法:

writeExternal()

用于将您的类写入流容器。

与实现接口的任何类一样,您的类必须定义此方法。

定义此方法时,请使用ObjectOutputStream类及其方法来简化流的编写。

readExternal()

用于从流容器读取序列化的类。

与实现接口的任何类一样,您的类必须定义此方法。

定义此方法时,请使用ObjectInputStream类及其方法来促进从流中读取。

现在,通过一些示例来了解所有这些内容。 在给定的类下面是Serialize类的示例...


import java.io.*;
public class SerializeClass implements Serializable { 
    private double i = 0;
    private String str = null;
    SerializeClass() {
        i = 49.90;
        str = "MySerializeClass Object";
    } 
    public double getDoubleValue(){
        return i;
    } 
    public String getStringValue(){
        return str;
    }
} 
Serializabe接口是Java IO包的一部分。 实现Serializabe接口的类必须声明为public(其原因很容易理解)。 该类的所有数据成员必须是原始的或可序列化的对象本身。我们可以轻松地将该类的对象保存到流中,例如:

String path = "e:/folder/file"//set path u want.
FileOutputStream fos = new FileOutputStream(path);//Creating a stream for writing.
ObjectOutputStream oos = new ObjectOutputStream(fos);//Creating a object to write to the file 
SerializeClass serializeObject = new SerializeClass();//SerializeClass Object
oos.writeObject(serializeObject);
oos.flush();//forces the data to get written to the stream. 
将对象写入文件后,以后您可以从文件中取回该对象,并使用附加到该对象的值。 可以这样完成:

FileInputStream fis = new FileInputStream(path);
ObjectInputStream ois = new ObjectInputStream(fis); 
serialObject = (SerializeClass)ois.readObject(); 
您可以使用此对象访问附加值:-

System.out.println(serialObject.getStringValue());    
System.out.println(serialObject.getDoubleValue()); 
Externalizable接口示例:

import java.io.*;
public class ExternalizeClass implements Externalizable { 
    private double i = 0; 
    private String str = null; 
    ExternalizeClass() {
        i = 99.0;
        str = "MyExternalize Object";
    } 
    public double getDoubleValue(){
        return i;
    } 
    public String getStringValue(){
        return str;
    } 
    //we must define these methods
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeDouble(this.i);
        out.writeObject(this.str);
    } 
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        this.i = in.readDouble();
        this.str = (String)in.readObject();
    }
} 
与我们对SerializeClass对象所做的操作相同,我们可以对ExternalizeClass对象进行操作以从文件进行读写。 在这里,我们必须使用writeExternal()和readExternal()方法。

我们已经学习了如何向流中写入和读取序列化对象。 如果要防止某些字段存储在序列化对象中,那该怎么办? 通过在变量声明中的数据类型之前放置关键字transient,可以很容易地做到这一点。 静态字段未序列化(写出),因此无法序列化(读回)。

完成上述任务的另一种方法是重写writeObject()和readObject()方法。 在这些方法的定义中,您可以控制不写入和读取哪个字段。

对于Externalize接口,由于必须同时将readExternal()和writeExternal()方法都声明为公共方法,因此这增加了对象可以使用它们确定序列化对象状态的风险。 因此,在使用此接口保存对象数据时应格外小心。

serialVersionUID

当对象存储了很长时间(恢复时)会发生什么?

它发现它的格式已被该类的新的不同版本所取代? 读取流考虑了任何此类差异。 目的是Java类的较新版本应能够与同一类的较旧表示形式进行互操作,只要该类结构没有某些更改即可。

因此,无论是在运行时还是在反序列化时,我们都会检查向后兼容性。

可以使用版本号指定对类的更改。 特定班级

变量serialVersionUID(代表流唯一标识符或SUID)可用于指定可以反序列化的类的最早版本。 SUID声明如下:


static final long serialVersionUID = 2L; 
该声明表明版本2可以追溯到此类为止。 它与该类的版本1编写的对象不兼容。 如果遇到版本1对象,它将抛出InvalidClassException。

如果未明确定义SUID,则将生成默认的SUID。 这个预设

SUID是散列或唯一数值,使用类计算

名称,接口,字段和方法。 如何在以下位置获取类的SUID

运行时确定兼容性? 您可以这样获得:


ObjectSteamClass myObject = ObjectStreamClass.lookUp(class.forName("Myclass");
long SUID = myObject.getSerialVersionUID(); 
这两行是做什么的? 他们向虚拟机查询信息

关于流中表示的类的信息,请使用该类的方法

ObjectStreamClass。

您如何确定可以在类版本之间进行哪些更改?

这些更改可能会影响对象的还原:

分别删除字段,或将其从非静态或非瞬态更改为静态或瞬态。

更改类在层次结构中的位置。

更改原始字段的数据类型。

将类的接口从“可序列化”更改为“可外部化”(反之亦然)。

这些对类的更改是可以接受的:

添加字段,这将导致还原时将默认值(基于数据类型)分配给新字段。

由于类结构信息包含在流中,因此添加类仍将允许创建所添加类的对象。 但是,其字段将设置为默认值。

添加或删除writeObject()或readObject()方法。

更改字段的访问修饰符(公共,私有等),因为仍然可以为该字段分配值。

将字段分别从静态或瞬态更改为非静态或非瞬态。

为了理解上面给出的概念,请尝试在上面给出的Serializable类中添加一个变量。


static final long serialVersionUID = 1L; 
添加两个新变量,例如。

private double j = 77.7; //&
private String str1 = "New String"; 
尝试通过序列化类对象访问这些变量。 它将为您提供这些对象的默认值,即,对于双精度对象为0,对于字符串为null,并且不会引发任何异常。 但是如果您将varialbe serialVersionUID更改为

2L,它将不会读取这两个新变量,并且将引发InvalidClassException。

From: https://bytes.com/topic/java/insights/703298-author-madhoriya22-serialization

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值