欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

JAVA八股文1——(三)序列化

时间:2023-08-05

本文是java八股文学习总结系列、内容来自于各学习文档总结。
一、序列化和反序列化
 1、概念
 把对象转换为字节序列的过程称为对象的序列化。
 把字节序列恢复为对象的过程称为对象的反序列化。
 2、用途
  1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
  2) 在网络上传送对象的字节序列。
3、API
 3.1、序列化
 java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
  3.2、反序列化
  java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
  3.3、实现方法
  只有实现了Serializable和Externalizable接口的类的对象才能被序列化。Externalizable接口继承自 Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以 采用默认的序列化方式 。
  3.4、对象序列化包括如下步骤:
   3.4.1、 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;
   3.4.2、通过对象输出流的writeObject()方法写对象。

3.5、对象反序列化的步骤如下:
   3.5.1、 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;
   3.5.2、 通过对象输入流的readObject()方法读取对象。
  3.6、示例代码:
  Person类:

复制代码 1 import java.io.Serializable; 3 10 public class Person implements Serializable {12 15 private static final long serialVersionUID = -5809782578272943999L;16 private int age;17 private String name;18 private String sex;20 public int getAge() {21 return age;22 }24 public String getName() {25 return name;26 }28 public String getSex() {29 return sex;30 }32 public void setAge(int age) {33 this.age = age;34 }36 public void setName(String name) {37 this.name = name;38 }40 public void setSex(String sex) {41 this.sex = sex;42 }43 }

序列化和反序列化方法

import java.io.File; 2 import java.io.FileInputStream; 3 import java.io.FileNotFoundException; 4 import java.io.FileOutputStream; 5 import java.io.IOException; 6 import java.io.ObjectInputStream; 7 import java.io.ObjectOutputStream; 8 import java.text.MessageFormat; 9 10 17 public class TestObjSerializeAndDeserialize {18 19 public static void main(String[] args) throws Exception {20 SerializePerson();//序列化Person对象21 Person p = DeserializePerson();//反序列Perons对象22 System.out.println(MessageFormat.format("name={0},age={1},sex={2}",23 p.getName(), p.getAge(), p.getSex()));24 }25 26 33 private static void SerializePerson() throws FileNotFoundException,34 IOException {35 Person person = new Person();36 person.setName("gacl");37 person.setAge(25);38 person.setSex("男");39 // ObjectOutputStream 对象输出流,将Person对象存储到E盘的Person.txt文件中,完成对Person对象的序列化操作40 ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(41 new File("E:/Person.txt")));42 oo.writeObject(person);43 System.out.println("Person对象序列化成功!");44 oo.close();45 }46 47 55 private static Person DeserializePerson() throws Exception, IOException {56 ObjectInputStream ois = new ObjectInputStream(new FileInputStream(57 new File("E:/Person.txt")));58 Person person = (Person) ois.readObject();59 System.out.println("Person对象反序列化成功!");60 return person;61 }62 63 }

3.7、异常
1、如果被写对象的类型是String,或数组,或Enum,或Serializable,那么就可以对该对象进行序列化,否则将抛出NotSerializableException
2、读取程序的CLASSPATH中包含有Person.class(哪怕在读取Person对象时并没有显示地使用Person类,如上例所示),否则会抛出ClassNotFoundException。
3.8、相关方法和关键字
3.8.1、transient
某个字段被声明为transient后,默认序列化机制就会忽略该字段

public class Person implements Serializable { transient private Integer age = null;}

3.8.2、writeObject()方法与readObject()方法

public class Person implements Serializable { transient private Integer age = null; private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeInt(age); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); age = in.readInt(); }}

在writeObject()方法中会先调用ObjectOutputStream中的defaultWriteObject()方法,该方法会执行默认的序列化机制,因为age使用了transcient关键字,此时会忽略掉age字段。然后再调用writeInt()方法显示地将age字段写入到ObjectOutputStream中。readObject()的作用则是针对对象的读取,其原理与writeObject()方法相同。
其中writeObject()与readObject()都是private方法,调用他们要使用反射。
3.8.3、Externalizable接口
无论是使用transient关键字,还是使用writeObject()和readObject()方法,其实都是基于Serializable接口的序列化。JDK中提供了另一个序列化接口–Externalizable,使用该接口之后,之前基于Serializable接口的序列化机制就将失效。此时将Person类修改成如下,

public class Person implements Externalizable { private String name = null; transient private Integer age = null; private Gender gender = null; public Person() { System.out.println("none-arg constructor"); } public Person(String name, Integer age, Gender gender) { System.out.println("arg constructor"); this.name = name; this.age = age; this.gender = gender; } private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeInt(age); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); age = in.readInt(); } @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(name); out.writeInt(age); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { name = (String) in.readObject(); age = in.readInt(); }}

3.8.4readResolve()方法
当我们使用Singleton模式时,应该是期望某个类的实例应该是唯一的,但如果该类是可序列化的、则从文件person.out中获取的Person对象与Person类中的单例对象并不相等。为了能在序列化过程仍能保持单例的特性,可以在Person类中添加一个readResolve()方法,在该方法中直接返回Person的单例对象,如下所示:

public class Person implements Serializable { private static class InstanceHolder { private static final Person instatnce = new Person("John", 31, Gender.MALE); } public static Person getInstance() { return InstanceHolder.instatnce; } private String name = null; private Integer age = null; private Gender gender = null; private Person() { System.out.println("none-arg constructor"); } private Person(String name, Integer age, Gender gender) { System.out.println("arg constructor"); this.name = name; this.age = age; this.gender = gender; } private Object readResolve() throws ObjectStreamException { return InstanceHolder.instatnce; }}

无论是实现Serializable接口,或是Externalizable接口,当从I/O流中读取对象时,readResolve()方法都会被调用到。实际上就是用readResolve()中返回的对象直接替换在反序列化过程中创建的对象,而被创建的对象则会被垃圾回收掉。

Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。