序列化流:
序列化:将一个对象转换成网络中传输的流
对象输出流:ObjectOutputStream
反序列化:将网络中传输的流还原成一个对象
对象输入流:ObjectInputStream
一个类对象将来要想被序列化,必须要实现Serializable接口,这个接口中没有任何的方法和常量,称为标记接口。
InvalidClassException异常
我们在写完对象后,又修改了类中的内容,又重新写入,再读取的还原对象的时候,报错了InvalidClassException异常 这是因为修改后的UID发生了改变,序列化的对象UID:stream classdesc serialVersionUID = -7948663535168613063, 本地修改后的UID: local class serialVersionUID = 4202380653329948443, 因为没有固定UID,所以每次修改完,UID都会变 解决方案:在Students类里将serialVersionUID写固定,将来谁都不能改,就不会出现修改后错误,不用手写,自动生成即可。
第一步:直接点击异常类
第二步:复制UID
第三步:将复制的UID写入Students类中
第四步:需要重新运行一次程序,只调用write方法,等于将固定UID写入文本中
第五步:修改变量,或者增加其他变量
第六步:修改变量后,只调用read方法,程序也不会报错
总结:序列化对象,要一边修改,一边写入一边读,就不会出现UID不一样的错误
序列化和反序列化使用参考源码:
Students类:
import java.io.Serial;
import java.io.Serializable;
//TODO: 实现序列化需要实现一个Serializable 标记接口(所谓标记接口就是没有任何成员方法和变量,就只是作标记)
//TODO: transient修饰成员变量可以防止被序列化
// 我们在写完对象后,又修改了类中的内容,再读取的还原对象的时候,报错了InvalidClassException
// stream classdesc serialVersionUID = 2442942279365203766,每次写完UID都会变
// local class serialVersionUID = 2935475373948736279
//
// TODO: 解决方案:将serialVersionUID写固定,将来谁都不能改,自动生成即可。
//
//TODO: 若成员不想被序列化存储,使用java提供一个关键字进行修饰 transient
public class Students implements Serializable {
@Serial
private static final long serialVersionUID = -4333316296251054416L;
private String name;
private int age;
private String address;
private int caiFu;
public Students() {
}
public Students(String name, int age, String address,int caiFu) {
this.name = name;
this.age = age;
this.address = address;
}
public String getName() {
return name;
}
public int getCaiFu() {
return caiFu;
}
public void setCaiFu(int caiFu) {
this.caiFu = caiFu;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Students{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
", caiFu=" + caiFu +
'}';
}
}
测试类:
import java.io.*;
//TODO:关于序列化流,正常写入文本的对象是什么内容,读出来的也就是什么内容,如果临时修改成员方法,并未及时写入原来的文本中
//TODO:这时候再次读就会报错,原因是每次修改后的序列化UID都会发生改变,所以要想正常读出来,则必须固定UID,但是读出来的新的参数的值是默认值,
//TODO:因为没有立即将新内容写入进去。另外被transient修饰的成员变量不会被序列化,就是普通变量,不会对序列化产生影响
//TODO:总结:序列化对象,要一边修改,一边写入一边读,就不会出现UID不一样的错误
public class XuLeiHuaTest {
public static void main(String[] args) {
// write();
read();
}
public static void write(){
//序列化流(输出流)
ObjectOutputStream ous=null;
try {
ous = new ObjectOutputStream(new FileOutputStream("src/main/java/day15_8_13/序列化流.txt"));
Students s1 = new Students("小伟",18,"山东菏泽",12000);
ous.writeObject(s1);//将对象序列化
ous.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
//用完就释放资源
ous.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void read(){
//反序列化流(输入流)
ObjectInputStream ois=null;
try {
ois = new ObjectInputStream(new FileInputStream("src/main/java/day15_8_13/序列化流.txt"));
try {
Object o = ois.readObject();
//向下转型
Students s1 = (Students) o;
System.out.println(o);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
//用完释放资源
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}