序列化 ObjectOutputStream
反序列化 ObjectInputStream
序列化:Object OutputStream
ObjectOutputStream 将 Java 对象的基本数据类型写入 OutputStream,通过在流中使用文件可以实现对象的持久存储。如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象。
构造方法:
ObjectOutputStream(OutputStream out)
创建写入指定 OutputStream 的 ObjectOutputStream
普通方法:
writeObject(Object obj)
将指定的对象写入 ObjectOutputStream
反序列化:ObjectInputStream
ObjectInputStream对以前使用ObjectOutputStream写入的基本数据和对象进行反序列化重构对象。
构造方法:
ObjectInputStream(InputStream in) 创建从指定 InputStream 读取的 ObjectInputStream
普通方法:
readObject() 从 ObjectInputStream 读取对象
package partThree;
/*本类用于序列化测试的物科类*/
public class Student2 {//定义学生相关的属性并进行封装private String name; //姓名private int age; //年龄private String addr; //地址private char gender;//性别//2.1 创建本类的无参构造--注意必须手动提供无参构造,否则会被覆盖public Student2() {System.out.println("我是Student的无参构造");}//2.2创建全参构造public Student2(String name, int age, String addr, char gender) {super();//默认调用父类的无参构造this.name = name;this.age = age;this.addr = addr;this.gender = gender;System.out.println("我是Student的全参构造");}//3.属性封装后,需要本类提供公共的属性访问与设置方式get()&set()/*** 自动创建get()&set(),右键-->Source-->Generate Getters and Setters...*/public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getAddr() {return addr;}public void setAddr(String addr) {this.addr = addr;}public char getGender() {return gender;}public void setGender(char gender) {this.gender = gender;}//如果不想查看对象的地址值,而是想查看类型 属性 属性值//可以在子类中添加重写的toString()@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", addr='" + addr + '\'' +", gender=" + gender +'}';}
}
package partThree;import java.io.*;/* 本类用于测试对象的序列化与反序列化* 序列化 ObjectOutputStream* 是指把程序中的java对象,通过序列化流oos输出到磁盘的文件中,相当于数据写出的过程* 反序列化 ObjectInputStream* 是指把之前已经保存在文件中的对象的相关数据,通过反序列化流ois读到内存中的过程中* 相当于数据读取的过程*/
public class TestSerializable {public static void main(String[] args) {method1(); //本方法用来完成序列化的功能method2(); //本方法用于完成反序列化功能}private static void method2() {//1.定义一个在本方法中都生效的局部变量,注意初始化ObjectInputStream in = null;//2.由于IO操作可能会产生异常,所以完成try-catch-finally结构try{//3.创建反序列化流oisin = new ObjectInputStream(new FileInputStream("E:\\ready\\1.txt"));//4.通过反序列化流,读取文件中的数据,并把读到的数据,回复成数据Object o = in.readObject();System.out.println(o); //将接到的对象打印System.out.println("恭喜您,反序列化成功!");}catch(Exception e){System.out.println("很抱歉,反序列化失败!");e.printStackTrace();}finally {try {in.close();} catch (IOException e) {e.printStackTrace();}}}private static void method1() {//定义一个在本方法中都生效的局部变量,注意初始化ObjectOutputStream out = null;try {//1.创建流对象--需要try-catch,需要设置FileOutputStream子类,// 并设置传输文件路径表明要把序列化后的对象数据输出到哪个文件中out = new ObjectOutputStream(new FileOutputStream("E:\\ready\\1.txt"));//2.使用流对象//2.1 指定一个要序列化输出的对象,并设置属性Student2 s = new Student2("海绵宝宝",3,"海里",'男');//2.2 序列化输出对象s到文件中out.writeObject(s);System.out.println("恭喜您,序列化成功!");//成功后,我们可以在目标文件里看到序列化输出的数据,但注意,这个数据是为了底层保存对象和传输使用的,我们看不懂//有些类似于我们字节码文件中的数据} catch (IOException e) {System.out.println("很抱歉,序列化失败!");e.printStackTrace();}finally {//3.关闭流对象try {out.close();} catch (IOException e) {e.printStackTrace();}}}
}
NotSerializableException:报错原因:要序列化对象所在的类并没有实现序列化接口
解决方案:实现序列化接口
InvalidClassException:报错原因:更改子类数据后,会导致本次反序列化时使用的UID与序列化时的UID不匹配
解决方案:反序列化时的UID与序列化时的UID要保持一致,或者测试时一次序列操作对应一次反序列化操作,否则不匹配就报错,重新进行一次序列化后,再进行反序列化即可;
我们在反序列化时,JVM会拿着反序列化流中的serialVersionUID与序列化时相应的实体类中的serialVersionUID来比较,如果不一致,就无法正常反序列化,出现序列化版本不一致的异常InvalidClassException。
而且我们在定义需要序列化的实体类时,如果没有手动添加UID,
Java序列化机制会根据编译的class自动生成一个,那么只有同一次编译生成的class才是一样的UID。
如果我们手动添加了UID,只要这个值不修改,就可以不论编译次数,进行序列化和反序列化操作。