java序列化
1.序列化的类要继承Serializable接口。其父类也需要实现这个接口。否则反序列化抛异常。
2.序列化类A,引用了另外一个类B的实例,那么类B也需要实现接口。如果两个A的实例,引用了同一个B实例,那么B只序列化一次。
3.A实例序列化后,反序列化的A的实例,与原实例是两个对象。这点要注意,反序列化过程不调用构造函数,因此单例模式时,会出问题。这时需要重写readResolve方法。一般此方法为private方法,因为如果父类有此方法,子类没有重写,那么子类反序列化将得到父类对象实例。
4.可以设置序列化的过滤器。反序列化时,会自动调用过滤器的inputcheck方法。
5.可以自定义序列化。有两种方法
一种是在序列化类中写writeObject和readObject方法。这种方法,反序列化时不会调用构造函数。前者控制序列化时的操作,后者是前者的反操作。也可以写writeReplace方法。这个方法可以将对象转换成其他对象。反序列化时会先调用writeObject,如果该方法返回的另外一个对象也有这个方法则继续调用,直到没有,然后再调用readObject方法。
另一种方法,实现Externailzable接口。这个接口强制实现自定义序列化。性能较第一种略好。这个接口有两个方法
void readExternal,和writeExternal方法。除了函数签名与第一种不一样,其他用法一样。这个方法的反序列化会调用无参构造函数。因此一定要有无参的构造函数。
第二种方法因为强制实现自定义序列,编程略麻烦,因此一般使用第一种方法序列化。
6.对象的类名,实例变量,都会被序列化,方法,类变量,transient实例变量(瞬态变量)都不会被实例化。如果想让某个实例变量不要序列化,应该加上transient修饰符。不应该加staic。
7.序列化还有一个serialVersionUID指定class文件的版本号。如果版本号没变,但是class变量变了。仍然可以反序列化。(如果修改了实例变量,那么则应该修改这个版本号)
package objStream; import java.io.FileInputStream; import java.io.ObjectInputFilter; import java.io.ObjectInputStream; public class FilterTest { public static void main(String[] args) { try( var oid=new ObjectInputStream(new FileInputStream("out1.txt")); ){ //为序列化设置过滤器。当反序列化时,过滤器的checkInput方法会被自动激发。 //1、用lambda表达式,因为ObjectInputFilter是一个函数式接口。需要实现checkInput方法。这个方法有一个info参数。所以info->{}。 oid.setObjectInputFilter(filterInfo -> { //2、重写checkinput方法,首先要遵循他默认的checkinput结果。因此要先得到一个ObjectInputFilter对象。 var serialFilter=ObjectInputFilter.Config.getSerialFilter(); if(serialFilter!=null) { //3、利用已有的过滤器,得到默认的status结果。如果结果不是undecide,就返回结果。 var status = serialFilter.checkInput(filterInfo); if (status != ObjectInputFilter.Status.UNDECIDED) { return status; } } //4、如果是undecided,继续判断。此处为拓展原来的。 if(filterInfo.references()!=1) { return ObjectInputFilter.Status.REJECTED; } if(filterInfo.serialClass()!=null&&filterInfo.serialClass()!=Person.class){ return ObjectInputFilter.Status.REJECTED; } //5、如果都不是以上情况,继续返回不确定。 return ObjectInputFilter.Status.UNDECIDED; }); }catch (Exception ex){ ex.printStackTrace(); } } }
上面代码时设置序列化过滤器。
package objStream; import java.io.*; public class sameTest { public static void main(String[] args) { Teacher teacher=new Teacher("张三"); var stu1=new Student("李四",teacher); var stu2=new Student("王五",teacher); try { var oos=new ObjectOutputStream(new FileOutputStream("same.txt")); var ois=new ObjectInputStream(new FileInputStream("same.txt")); oos.writeObject(teacher); oos.writeObject(stu1); oos.writeObject(stu2); var teacher1=(Teacher) ois.readObject(); var stu11=(Student)ois.readObject(); var stu22=(Student)ois.readObject(); System.out.println(teacher1); System.out.println(teacher); System.out.println(stu1); System.out.println(stu2); System.out.println(stu11); System.out.println(stu22); System.out.println("stu1,stu11----"+ stu1.equals(stu11)); System.out.println("stu2,stu22----"+ stu2.equals(stu22)); System.out.println("stu1.teac,teac----"+ stu1.getTeacher().equals(teacher));//true System.out.println("stu1.teac,stu11.teac----"+ stu1.getTeacher().equals(stu11.getTeacher()));//false System.out.println("stu1.teac,stu2.teac----"+ stu1.getTeacher().equals(stu2.getTeacher()));//true System.out.println("stu11.teac,stu22.teac----" +stu11.getTeacher().equals(stu22.getTeacher()));//true System.out.println("teac,teac1----" +teacher.equals(teacher1)); //得出结论,反序列化后的对象和原对象不是一个对象。 //对于引用同一个对象的实例的两个不同对象,引用的同一个实例只序列化一次,所以这个实例是同一个。 //反序列化时,不经过构造器,因此如果是单例或者是枚举类,则需要重写readResolve方法,以保证反序列化以后,还是原来的实例。否则将会生成新的实例 } catch (Exception e) { e.printStackTrace(); } } } class Teacher implements Serializable{ public Teacher(String name){ this.name=name; } private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Teacher{" + "name='" + name + '\'' + '}'; } } class Student implements Serializable{ public Student(String name,Teacher teacher){ this.name=name; this.teacher=teacher; } private String name; private Teacher teacher; public String getName() { return name; } public void setName(String name) { this.name = name; } public Teacher getTeacher() { return teacher; } public void setTeacher(Teacher teacher) { this.teacher = teacher; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", teacher=" + teacher + '}'; } }
上面代码运行结果是