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

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

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

本文是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()中返回的对象直接替换在反序列化过程中创建的对象,而被创建的对象则会被垃圾回收掉。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5722429.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-18
下一篇 2022-12-18

发表评论

登录后才能评论

评论列表(0条)

保存