如何验证反序列成功了呢?那就将他的属性打印出来,如果是我们最初定义的值,那么就是成功的
注意问题一:版本号UID,也就是当一个文件被序列化之后,然后再类里面添加其他属性的时候,此时反序列化的结果是读取不到新添加的属性的值
那么如何解决这个自定义类不一致的问题呢?就需要使用UID,可以看到,如果不定义一个UID那么Java内部会自定义的设置两个不同的UID
在Person里面定义一个serialVersionUID
测试代码:
测试步骤:
1.进行序列化操作
2.添加新的属性name
3.进行反序列化查看结果(结果是对应的属性,如String就会返回null)
序列化部分
import java.io.*;
/**
- 版本UID问题
*/
public class IoDemo12 {
static class Person implements Serializable {
public final static long serialVersionUID = 1L;
private int id;
private int age;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public static void main(String[] args) {
Person person = new Person();
person.setId(111);
person.setAge(18);
person.setName(“豹子头”);
//需要将二进制存在哪里的路径
String filePath = “D:\io_test\2\person1”;
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(filePath));
{
//进行序列化操作
objectOutputStream.writeObject(person);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
反序列化部分
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
public class IoDemo13 {
public static void main(String[] args) {
IoDemo12.Person person = null;
String filePath = “D:\io_test\2\person1”;
try{
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filePath));
{
person = (IoDemo12.Person) objectInputStream.readObject();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(“person id :”+person.getId());
System.out.println(“person name :”+person.getName());
}
}
执行结果
总结:
serialVersionUID可以控制版本了,当出现版本不一致的情况,就能够迅速定位到问题并解决,如果不设置UID(不修改)就会造成,读取数据为(引用类型)null/(int)0的业务逻辑问题(假如时新的版本就可以正常读取到值,可以进行下面的业务逻辑,反之,如果时旧版本的二进制序列化文件,可以进行舍弃)
1.当进行序列化和反序列化时显示的设置此值
2.每次对实体类进行修改之后更新UID的值
注意问题二:临时变量——transient
假如一个类里面有敏感信息,不想让他被序列化,那么就可以让他编程临时变量
注意问题三:static变量
为什么count的值已经被序列化了,但是还是能修改它的值呢?
-
因为static的变量在序列化的时候并没有保存
-
所有的而属性设置都是来描述对象(实例)——对象属性(可以被序列化)
-
静态变量是用来描述类的——类属性(不能被序列化)
结果:
当序列化之后再修改的静态属性值并没有被修改,而那些在序列化之后被修改的属性,再被序列化之后就会被覆盖(修改)掉了
问题四:序列化父类问题
-
如果子类实现了序列化的接口(Serializable) ,而父类没有实现序列化接口,就会出现子类的属性会进行序列化保存,而父类不会进行序列化数据保存
-
父类实现序列化的接口,所有子类都会正常序列化和反序列化
总结:
序列化和反序列化的注意点:
①序列化时,只对对象的状态进行保存,而不管对象的方法;
②当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口;
③当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化;
④并非所有的对象都可以序列化,至于为什么不可以,有很多原因了,比如:
安全方面的原因,比如一个对象拥有private,public等field,对于一个要传输的对象,比如写到文件,或者进行RMI传输等等,在序列化进行传输的过程中,这个对象的private等域是不受保护的;
资源分配方面的原因,比如socket,thread类,如果可以序列化,进行传输或者保存,也无法对他们进行重新的资源分配,而且,也是没有必要这样实现;
⑤声明为static和transient类型的成员数据不能被序列化。因为static代表类的状态,transient代表对象的临时数据。
⑥序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联,该序列号在反序列化过程中用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类。为它赋予明确的值。显式地定义serialVersionUID有两种用途:
-
在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;
-
在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。
⑦Java有很多基础类已经实现了serializable接口,比如String,Vector等。但是也有一些没有实现serializable接口的;
⑧如果一个对象的成员变量是一个对象,那么这个对象的数据成员也会被保存!这是能用序列化解决深拷贝的重要原因;
注意:浅拷贝请使用Clone接口的原型模式。
如果想要了解更多,可以点击这里
总结
总的来说,面试是有套路的,一面基础,二面架构,三面个人。
最后,小编这里收集整理了一些资料,其中包括面试题(含答案)、书籍、视频等。希望也能帮助想进大厂的朋友
sk-blog-2alltop_click~default-3-104003193.first_rank_v2_pc_rank_v29&utm_term=%E5%BA%8F%E5%88%97%E5%8C%96%E5%92%8C%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96&spm=1018.2226.3001.4187]( )
总结
总的来说,面试是有套路的,一面基础,二面架构,三面个人。
最后,小编这里收集整理了一些资料,其中包括面试题(含答案)、书籍、视频等。希望也能帮助想进大厂的朋友
[外链图片转存中…(img-pUrqgISe-1714338792366)]
[外链图片转存中…(img-bFGMHrwO-1714338792367)]