I学霸官方免费教程四十六 :Java基础之序列化和反序列化
序列化和反序列化
序列化:将对象当前状态转换为字节序列,并通过输出流存储或传输的过程
作用:将对象转换成输出流,永久保存或传输
条件:被序列化的对象的类,实现Serializable接口或Externalizable接口
实例一: package serializable.s1; import java.io.Serializable; /** * 用于创建被序列化对象的类 * @author 学霸联盟 - 赵灿 */ public class Son implements Serializable { // 年龄 public int age; // 颜值 public int faceValue; // 构造方法 public Son(int age, int faceValue) { this.age = age; this.faceValue = faceValue; } // 重写父类(Object类)的toString方法 @Override public String toString() { return "age = " + age + "\nfaceValue = " + faceValue; } }
package serializable.s1; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; /** * 序列化对象的类 * @author 学霸联盟 - 赵灿 */ public class SerializableObject { public static void main(String[] args) { //创建一个对象son Son son = new Son(18); //声明文件输出流的引用 FileOutputStream fos = null; //声明对象输出流的引用 ObjectOutputStream oos = null; try { //创建文件输出流的对象,其中文件的后缀名可以自定义 fos = new FileOutputStream("E:\\son.txt"); //创建对象输出流的对象 oos = new ObjectOutputStream(fos); //将对象写入文件,序列化之后son的age值应为20 oos.writeObject(son); System.out.println("序列化完成!"); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (oos != null) { //关闭对象输出流 oos.close(); } if (fos != null) { //关闭文件输出流 fos.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
反序列化:将序列化产生的字节序列,通过输入流读取,并恢复成对象的过程
package serializable.s1; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.ObjectInputStream; /** * 反序列化对象的类 * @author 学霸联盟 - 赵灿 */ public class RecoveryObject { public static void main(String[] args) { Son son = null; FileInputStream fis = null; ObjectInputStream ois = null; try { //创建文件输入流对象,这里的文件路径要和序列化时的一致 fis = new FileInputStream("E:\\son.txt"); ois = new ObjectInputStream(fis); //从对象输入流中读取对象,并强制转换为Son类型 son = (Son) ois.readObject(); //输出 System.out.println(son.toString()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { try { if(ois != null){ //关闭对象输入流 ois.close(); } if (fis != null) { //关闭文件输入流 fis.close(); } } catch (IOException e) { e.printStackTrace(); } } } } 输出结果: age = 18 faceValue = 250
注意:在序列化之后,如果对Son类进行了修改,而没有重新执行序列化操作;此时进行反序列化会出现InvalidClassException(无效的类型异常);
这是因为在序列化时,会给对象加上一个默认的版本号,这个版本号由各属性名,方法名等计算得出;在反序列化时,会验证序列化时产生的版本号,和当前类的版本号是否一致,如果不一致就会产生InvalidClassException
static final long serialVersionUID属性:序列化版本号
作用:用于反序列化时,验证反序列化的对象和当前类是否是同一版本
序列化版本号,可以自己写,也可以使用编译器自动生成的。
自己写这个版本号时要注意,只能写成static final long serialVersionUID这样的,版本号的值可以自定义。
使用Eclipse自动生成,如下图
当设定了版本号,在序列化之后,只要不修改类名和版本号,对类的其他修改,不会使反序列化出现InvalidClassException。
实例二: package serializable.s2; import java.io.Serializable; /** * 用于创建被序列化对象的类 * @author 学霸联盟 - 赵灿 */ public class Son implements Serializable { /** * 编译器自动生成的序列化版本号 */ private static final long serialVersionUID = 5430359291175859046L; public int age; //将实例一的Son类,增加版本号并执行序列化后,将属性faceValue该为fV在执行反序列化 public int fV; //增加name属性 String name; public Son(int age, int faceValue) { this.age = age; this.fV = faceValue; } @Override public String toString() { //增加name的输出 return "age = " + age + "\nfV = " + fV + "\nname = " + name; } } 运行结果: age = 18 fV = 0 name = null
由结果可以看到,被修改和新增的属性值,均为属性的数据类型的默认值。可以理解为删除了faceValue属性,而新增了属性fV和name;反序列化无法获取这些新增属性的值,因为序列化时还没有它们,所以这些属性的值为默认值
关键字transient
使用关键字transient修饰的属性,不会被序列化
实例三: package serializable.s3; import java.io.Serializable; /** * 用于创建被序列化对象的类 * @author 学霸联盟 - 赵灿 */ public class Son implements Serializable { public int age; //将实例一Son中的faceValue属性加上关键字transient修饰 public transient int faceValue; public Son(int age, int faceValue) { this.age = age; this.faceValue = faceValue; } @Override public String toString() { return "age = " + age + "\nfaceValue = " + faceValue; } } 运行结果: age = 18 faceValue = 0
faceValue的值为0,并不是250;这就是因为使用关键字transient修饰的属性,不会被序列化
序列化父类
使Son类继承Father类
实例四: package serializable.s4; import java.io.Serializable; /** * 用于创建被序列化对象的类,继承Father类 * @author 学霸联盟 - 赵灿 */ public class Son extends Father implements Serializable { public int age; public int faceValue; public Son(int age, int faceValue) { this.age = age; this.faceValue = faceValue; //对从Father类继承的money属性赋值 this.money = 200; } @Override public String toString() { return "age = " + age + "\nfaceValue = " + faceValue + "\nmoney = " + money; } } package serializable.s4; public class Father{ public int money; } 运行结果: age = 18 faceValue = 250 money = 0
package serializable.s4; import java.io.Serializable; //增加Father类对Serializable接口的实现 public class Father implements Serializable { public int money; } 运行结果: age = 18 faceValue = 250 money = 200
引用类型的属性
Son类中增加一个Friend类型的属性
实例五: package serializable.s5; import java.io.Serializable; /** * 用于创建被序列化对象的类 * @author 学霸联盟 - 赵灿 */ public class Son implements Serializable { public int age; public int faceValue; //增加Friend属性 public Friend friend; public Son(int age, int faceValue) { this.age = age; this.faceValue = faceValue; //创建Friend对象 friend = new Friend(); friend.name = "张三"; } @Override public String toString() { return "age = " + age + "\nfaceValue = " + faceValue + "\nfriendName = " + friend.name; } } package serializable.s5; public class Friend{ public String name; } 执行序列化操作时会出现NotSerializableException
package serializable.s5; import java.io.Serializable; //增加Friend类对Serializable接口的实现 public class Friend implements Serializable{ public String name; } 运行结果: age = 18 faceValue = 250 friendName = 张三
String类,数组,枚举都实现了Serializable接口,所以可以直接使用
自定义序列化
将实例五的Son该为实现Externalizable接口
实例六: package serializable.s6; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; /** * 用于创建被序列化对象的类 * @author 学霸联盟 - 赵灿 */ public class Son implements Externalizable { public int age; public transient int faceValue; public Friend friend; //必须包含public的无参构造方法,否则会出现InvalidClassException public Son() { } public Son(int age, int faceValue) { this.age = age; this.faceValue = faceValue; //创建Friend对象 friend = new Friend(); friend.name = "张三"; } /** * 重写Externalizable接口中定义的方法 * 向输出流中写属性值 */ @Override public void writeExternal(ObjectOutput out) throws IOException { //向输出流中写入属性age和faceValue的值 out.writeInt(age); out.writeInt(faceValue); } /** * 重写Externalizable接口中定义的方法 * 读取输入流中的属性值,并赋值给相应的属性 * 这里要注意,读属性值的顺序要和写属性值的顺序一致 */ @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { //从输入流中读取age和faceValue的值,并赋值给相应的属性 age = in.readInt(); faceValue = in.readInt(); } @Override public String toString() { return "age = " + age + "\nfaceValue = " + faceValue; } } package serializable.s6; public class Friend{ public String name; } 输出结果: age = 18 faceValue = 250
版权声明:本文为博主原创文章,未经博主允许不得转载。