六、序列化问题
6.1 采用默认序列化机制,类的静态字段会被序列化吗?
采用默认序列化机制进行序列化时,类的静态字段会被序列化吗,此时类的静态字段不会被序列化,当然,我们可以采用自定义序列化逻辑对静态变量进行序列化。
6.2 父类序列化问题
采用默认序列化机制序列化子类时,其父类的字段会被序列化吗?可以分为如下情形
1. 父类没有实现Serializable接口,没有提供默认构造函数
这时,反序列化会出错,提示没有提供正确的构造函数。修改Person类,代码如下
package com.hust.grid.leesf.serializable; import java.io.Serializable; class Human { private int number; public Human(int number) { this.number = number; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } public String toString() { return "number = " + number; } } public class Person extends Human implements Serializable { /** * */ private static final long serialVersionUID = 1L; private String name; private String gender; private int age; private transient Person friend; public Person(int number, String name) { super(number); this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Person getFriend() { return friend; } public void setFriend(Person friend) { this.friend = friend; } @Override public String toString() { return super.toString() + ", name = " + name + ", gender = " + gender + ", age = " + age + ", friend info is [" + friend + "]"; } }
测试类的的代码如下
复制代码 package com.hust.grid.leesf.serializable; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class SerializableDemo { public static void main(String[] args) throws Exception { Person leesf = new Person(1, "leesf"); Person dyd = new Person(2, "dyd"); leesf.setAge(24); leesf.setGender("man"); leesf.setName("leesf"); dyd.setAge(24); dyd.setGender("woman"); dyd.setName("dyd"); leesf.setFriend(dyd); dyd.setFriend(null); File file = new File("test"); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file)); oos.writeObject(leesf); oos.flush(); oos.close(); System.out.println(leesf); ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file)); leesf = (Person) ois.readObject(); ois.close(); System.out.println(leesf); } }
运行结果
number = 1, name = leesf, gender = man, age = 24, friend info is [number = 2, name = dyd, gender = woman, age = 24, friend info is [null]] Exception in thread "main" java.io.InvalidClassException: com.hust.grid.leesf.serializable.Person; no valid constructor at java.io.ObjectStreamClass$ExceptionInfo.newInvalidClassException(ObjectStreamClass.java:150) at java.io.ObjectStreamClass.checkDeserialize(ObjectStreamClass.java:768) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1775) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371) at com.hust.grid.leesf.serializable.SerializableDemo.main(SerializableDemo.java:32)
说明:可以看出是没有提供合法的构造函数。
2. 父类没有实现Serializable接口,提供默认构造函数
第一步中出现了错误,此时,我们修改Person类,代码如下
package com.hust.grid.leesf.serializable; import java.io.Serializable; class Human { private int number; public Human() { } public Human(int number) { this.number = number; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } public String toString() { return "number = " + number; } } public class Person extends Human implements Serializable { /** * */ private static final long serialVersionUID = 1L; private String name; private String gender; private int age; private transient Person friend; public Person() { super(); } public Person(int number, String name) { super(number); this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Person getFriend() { return friend; } public void setFriend(Person friend) { this.friend = friend; } @Override public String toString() { return super.toString() + ", name = " + name + ", gender = " + gender + ", age = " + age + ", friend info is [" + friend + "]"; } }
说明:给Human类提供了无参构造函数。测试类代码不变,运行结果如下
number = 1, name = leesf, gender = man, age = 24, friend info is [number = 2, name = dyd, gender = woman, age = 24, friend info is [null]] number = 0, name = leesf, gender = man, age = 24, friend info is [null]
说明:此时,我们可以看到,可以进行反序列化了,但是父类的number字段被赋值为int的默认值0,Person类的transient字段没有被序列化。
3. 父类实现Serializable接口
当父类实现Serializable接口时,修改Person类代码如下
package com.hust.grid.leesf.serializable; import java.io.Serializable; class Human implements Serializable { /** * */ private static final long serialVersionUID = 1L; private int number; public Human() { } public Human(int number) { this.number = number; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } public String toString() { return "number = " + number; } } public class Person extends Human implements Serializable { /** * */ private static final long serialVersionUID = 1L; private String name; private String gender; private int age; private transient Person friend; public Person() { super(); } public Person(int number, String name) { super(number); this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Person getFriend() { return friend; } public void setFriend(Person friend) { this.friend = friend; } @Override public String toString() { return super.toString() + ", name = " + name + ", gender = " + gender + ", age = " + age + ", friend info is [" + friend + "]"; } }
测试类的代码不变,运行结果如下
number = 1, name = leesf, gender = man, age = 24, friend info is [number = 2, name = dyd, gender = woman, age = 24, friend info is [null]] number = 1, name = leesf, gender = man, age = 24, friend info is [null]
说明:从结果可知,已经可以进行正确的序列化与反序列化了,子类的transient字段没有被序列化。
6.3 共享对象序列化问题
当序列化的两个对象都包含另外一个对象的引用时,在反序列化时,另外一个对象只会出现一次吗?修改Person类代码如下
package com.hust.grid.leesf.serializable; import java.io.Serializable; public class Person implements Serializable { /** * */ private static final long serialVersionUID = 1L; private String name; private String gender; private int age; private transient Person friend; public Person() { } public Person(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Person getFriend() { return friend; } public void setFriend(Person friend) { this.friend = friend; } }
测试类代码如下
package com.hust.grid.leesf.serializable; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.List; public class SerializableDemo { @SuppressWarnings("unchecked") public static void main(String[] args) throws Exception { Person leesf = new Person("leesf"); Person dyd = new Person("dyd"); Person lr = new Person("lr"); leesf.setAge(24); leesf.setGender("man"); dyd.setAge(24); dyd.setGender("woman"); lr.setAge(25); lr.setGender("man"); leesf.setFriend(dyd); lr.setFriend(dyd); dyd.setFriend(null); List<Person> persons = new ArrayList<Person>(); persons.add(leesf); persons.add(dyd); persons.add(lr); ByteArrayOutputStream bos1 = new ByteArrayOutputStream(); ByteArrayOutputStream bos2 = new ByteArrayOutputStream(); ObjectOutputStream oos1 = new ObjectOutputStream(bos1); oos1.writeObject(persons); oos1.writeObject(persons); ObjectOutputStream oos2 = new ObjectOutputStream(bos2); oos2.writeObject(persons); ByteArrayInputStream bis1 = new ByteArrayInputStream(bos1.toByteArray()); ByteArrayInputStream bis2 = new ByteArrayInputStream(bos2.toByteArray()); ObjectInputStream ois1 = new ObjectInputStream(bis1); ObjectInputStream ois2 = new ObjectInputStream(bis2); List<Person> persons1 = (List<Person>) ois1.readObject(); List<Person> persons2 = (List<Person>) ois1.readObject(); List<Person> persons3 = (List<Person>) ois2.readObject(); System.out.println(persons1); System.out.println(persons2); System.out.println(persons3); } }
运行结果如下
[com.hust.grid.leesf.serializable.Person@7f31245a, com.hust.grid.leesf.serializable.Person@6d6f6e28, com.hust.grid.leesf.serializable.Person@135fbaa4] [com.hust.grid.leesf.serializable.Person@7f31245a, com.hust.grid.leesf.serializable.Person@6d6f6e28, com.hust.grid.leesf.serializable.Person@135fbaa4] [com.hust.grid.leesf.serializable.Person@45ee12a7, com.hust.grid.leesf.serializable.Person@330bedb4, com.hust.grid.leesf.serializable.Person@2503dbd3]
说明:从结果可知,oos1执行的writeObject是向同一个内存空间写了两次,从结果可看出,两次写入的对象的地址空间都是一样的,即进行了浅拷贝。而oos2执行的writeObject是向另外一个内存空间写了一次,从结果可看出,因为对象的地址不同于之前的对象地址,即采用了深拷贝。
七、总结
写到这里,关于Java中的序列化与反序列化机制就已经分析完了,经过此次分析,对序列化机制的认识更加深刻。学习一个知识点,就要认认真真,踏踏实实的弄懂一个知识点,写博客就是一个特别好的方式。谢谢各位园友的观看~