当遇到这个Java序列化接口时,我们可能会遇到以下问题
当我第一次看到这个关键词时,我有以下疑问。
在处理这个问题之前,首先要了解一个问题,这是比较重要的。
这个接口,以及相关的东西,都是Java io中的。
1. 序列化和反序列化的概念
以上是专业的解释,现在我们来通俗地解释一下。 代码运行的时候,我们可以看到很多对象(都已经调试过了),这些对象可以是一个,也可以是某个类型的对象的集合,很多对象数据,而在这些数据中,有一些信息,我们想要永久保存,那么这就叫序列化。
它是将内存中的这些对象转换为一系列字节的过程。 最常见的就是把它变成一个文件。 我可以保存文件而无需序列化。 有什么影响? 我也是这么问的。
2、什么情况下需要序列化?
当你想将内存中的对象状态保存到文件或数据库时;
当你想使用套接字通过网络发送对象时;
当你想通过RMI传输对象时;
(老实说,在上面的内容中,我可能只使用了存储数据库的内容。)
3. java中如何实现序列化
只需实现接口即可
上述理论都比较简单。 让我们看一下下面的实际代码,看看这个序列化可以做什么以及它会导致什么错误。
让我们从目标代码开始,.java
package com.lxk.model;
import java.io.Serializable;
/**
* @author lxk on 2017/11/1
*/
public class FlyPig implements Serializable {
//private static final long serialVersionUID = 1L;
private static String AGE = "269";
private String name;
private String color;
transient private String car;
//private String addTip;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getCar() {
return car;
}
public void setCar(String car) {
this.car = car;
}
//public String getAddTip() {
// return addTip;
//}
//
//public void setAddTip(String addTip) {
// this.addTip = addTip;
//}
@Override
public String toString() {
return "FlyPig{" +
"name='" + name + '\'' +
", color='" + color + '\'' +
", car='" + car + '\'' +
", AGE='" + AGE + '\'' +
//", addTip='" + addTip + '\'' +
'}';
}
}
注意,后面的各种情况都会用到注释掉的代码。
这是主要方法
package com.lxk.test;
import com.lxk.model.FlyPig;
import java.io.*;
/**
* 序列化测试
*
* @author lxk on 2017/11/1
*/
public class SerializableTest {
public static void main(String[] args) throws Exception {
serializeFlyPig();
FlyPig flyPig = deserializeFlyPig();
System.out.println(flyPig.toString());
}
/**
* 序列化
*/
private static void serializeFlyPig() throws IOException {
FlyPig flyPig = new FlyPig();
flyPig.setColor("black");
flyPig.setName("naruto");
flyPig.setCar("0000");
// ObjectOutputStream 对象输出流,将 flyPig 对象存储到E盘的 flyPig.txt 文件中,完成对 flyPig 对象的序列化操作
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("d:/flyPig.txt")));
oos.writeObject(flyPig);
System.out.println("FlyPig 对象序列化成功!");
oos.close();
}
/**
* 反序列化
*/
private static FlyPig deserializeFlyPig() throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("d:/flyPig.txt")));
FlyPig person = (FlyPig) ois.readObject();
System.out.println("FlyPig 对象反序列化成功!");
return person;
}
}
简单解释一下上面两个操作文件流的类
表示对象输出流:
它的 (obj) 方法可以序列化参数指定的 obj 对象,并将结果字节序列写入目标输出流。
表示一个对象输入流:
它的 () 方法从源输入流中读取字节序列,将它们反序列化为对象,然后返回它。
具体如何查看运行状态。
第一个:只要拿出这些代码,不动地运行它们,看看效果。
实际运行结果会生成为d:/.txt文件。
从运行结果来看:
第二种方法:为了验证这个静态属性是否可以序列化和反序列化,可以执行以下操作。
public static void main(String[] args) throws Exception {
serializeFlyPig();
//FlyPig flyPig = deserializeFlyPig();
//System.out.println(flyPig.toString());
}
这完成之后,就意味着你首先将对象序列化到文件中。 该对象具有静态变量。
现在将类中AGE的值修改为26。
然后,看看下图的运行代码和执行结果。
可以看到,刚刚序列化的269并没有被读出。 就是刚刚修改的26。 如果可以的话,这个26应该被覆盖掉,应该是269。
因此,断定这个静态属性没有被序列化。
第三种:演示这个的功能和用法
最暴力的修改就是直接去掉模型类实现的接口。 然后执行后续的序列化和反序列化方法。 直接报错。
抛出异常:
这个太暴力了,不推荐。
然后,与上面的操作类似。 首先,单独执行序列化方法。 生成文件。
然后,打开属性,然后再次执行反序列化方法即可看到现象。
抛出异常:n 详细信息如下。
InvalidClassException: com.lxk.model.FlyPig;
local class incompatible:
stream classdesc serialVersionUID = -3983502914954951240,
local class serialVersionUID = 7565838717623951575
解释:
因为我没有在模型中显式地给this赋值,但是Java会自动给我赋值。 该值的计算与模型的属性相关。
我保存的时候,也就是序列化的时候,当时这个属性是不存在的。 因此,自动生成的值与我反序列化时Java自动生成的值不同,并且抛出了异常。 拉。
(也可以反过来,用ID序列化,然后不带ID反序列化。同样的问题。)
我们再来一遍,即先序列化。 此时打开评论最后long = 1L; 行代码。 先注释掉该属性,序列化后,打开该属性,再次反序列化。 走着瞧吧。
此时代码执行正常,一切正常。 好的。 序列化时,该属性丢失。 序列化时,对应的模型有一个附加属性。 但是反序列化执行正常,没有出现异常。
这种现象对我们意味着什么:
老铁,这个意义重大。 首先,如果你不知道这个序列化是做什么用的,万一他真的像开头说的那样保存数据库呢(这个保存db是否涉及到Java的序列化大概要看什么数据库?),传输,rmi传输。 虽然我不知道这是干什么用的。 您只需为模型 bean 实现此接口即可。 如果不写这个,那么以后扩展的时候,可能会出现不识别旧数据的bug,就会爆炸。 回忆一下上面的错误情况。 想想都觉得可怕。 谁来承担责任?
因此,有一种理论是,在实现这个接口的时候,必须要给它赋值。 这就是问题。
这也解释了为什么我们刚开始编码的时候,实现这个接口后,编辑器给出了黄色警告,需要添加这个ID的值。 这是一长串数字,你甚至不知道它们来自哪里。
下面说明如何设置这个值才可以。
首先,你不需要自己赋值,Java会帮你赋值。 但是这样会导致上面的bug,非常不安全,所以必须手动进行。
那么,我应该如何赋值呢? 它可能会自动为您分配一长串号码。 这是没有必要的。
您可以简单地指定 1L 的值,仅此而已。 。 这可以确保在代码一致的情况下反序列化成功。
不同的值会影响反序列化,即数据读取。 如果写1L,请注意L更大。 计算机不区分大小写,但作为查看者,我们需要区分1和L,所以没事的时候不要乱乱这个值。 否则,当版本升级时,旧数据将不兼容。 还是不知道问题出在哪里。 **。
以下是取自jdk api文档的接口描述。
一个类实现了 java.io. 接口以启用其序列化功能。 未实现此接口的类将无法序列化或反序列化其任何状态。 可序列化类的所有子类型本身都是可序列化的。 因为实现接口也间接相当于继承。 序列化接口没有方法或字段,仅用于标识可序列化语义。
描述关于
(注意对比,这张截图中的两段对应下面的两段中文,如果你仔细看这两段,就可以解释43楼的问题了。静态属性不会被序列化,但是有一个特殊的静态属性会被序列化,没办法,这个静态属性是原生的,自带的。)
序列化运行时使用称为“兼容类”的版本号与每个可序列化类关联。 如果接收方加载的对象类的版本号与相应发送方的类的版本号不同,则反序列化将导致 n。 可序列化类可以通过声明名为“”的字段(必须是 ()、final(long) 字段)来显式声明自己:
如果未显式声明可序列化类,则序列化运行时会根据该类的各个方面计算该类的默认值,如“Java(TM) 对象序列化规范”中所述。 但是,强烈建议所有可序列化类显式声明值,因为计算默认值对类的细节高度敏感,并且可能会根据编译器实现而有很大差异,这可能会在反序列化期间导致问题。 导致意外的 n.
因此,为了确保该值在不同的 Java 编译器实现中保持一致,序列化类必须声明一个显式值。 还强烈建议对显式声明使用修饰符(如果可能),原因是此类声明只能用于直接声明类——字段不能用作继承成员。 数组类无法声明显式的值,因此它们始终具有默认的计算值,但数组类不要求匹配这些值。
来源:/20131.html