Java攻城狮的入门课程Java学习笔记程序员

(十二)对象的序列化和反序列化

2017-03-17  本文已影响66人  黒猫

1、基本介绍

1.对象的序列化:将Object类型的对象转换成byte序列,简单来讲就是要存储对象,将对象写入文件。
序列化流(ObjectOutputStream),核心方法writeObject()

2.对象的反序列化:将byte序列转换成Object类型的对象,简单来讲就是从文件中读取对象。
反序列化流(ObjectInputStream),核心方法readObject()

3.序列化接口:对象必须实现序列化接口(Serializable),才能进行序列化,否则将出现异常。该接口中没有任何方法,只是一个标准。

注意:

当一个类实现了序列化接口,其子类都可以实现序列化;

对子类对象实现反序列化操作时,如果其父类没有实现序列化接口,那么其父类的构造方法会被调用,不会进行反序列化。


2、实现对象的序列化——写入对象

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;

// 创建Student类并实现Serializable接口,准备将该类的对象写入文件
class Student implements Serializable {
    private String name;
    private int age;

    public Student() {

    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

public class Demo1 {

    public static void main(String[] args) throws IOException {

        fun1();
    }

    public static void fun1() throws IOException {

        /*
         * 根据其构造方法 
         * ObjectOutputStream(OutputStream out) 
         * 传入的参数是OutputStream类型的
         * 因为多态的存在 
         * 所以OutputStream类的子类类型都可以
         */

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("f:\\Demo1.txt"));
        Student s1 = new Person("Tom", 20);
        oos.writeObject(s1);
        oos.close();
    }

}


3、实现对象的反序列化——读取对象

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class Demo2 {

    public static void main(String[] args) throws ClassNotFoundException, IOException {

        fun2();
    }

    public static void fun2() throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("f:\\Demo1.txt"));
        // 直接向下强制转型为Student类

        /*
         * 注意readObject()方法会抛出两个异常:
         * IOException,ClassNotFoundException
         * 之所以会有ClassNotFoundException 
         * 是因为对象是依赖于其所属类的字节码文件(类名.class)的
         * 因此在读取对象时,
         * 必须要求其所属类的字节码文件也存在
         */

        Student obj = (Student) ois.readObject();
        System.out.println(obj.getName() + "," + obj.getAge());
        ois.close();
    }
}


4、Serializable接口

1.序列号:

在写入对象时,对象所属的类实现的Serializable接口会在该类内部同时写入一个唯一的UID序列号,以便找到该类;如果在写入对象完成之后,修改了该类中的任意内容,那么序列号也会随之改变,会导致找不到该类,从而引发异常。因此在处理对象的序列化时,建议声明自己的UID,例如:

static final long serialVersionUID = 42L; 

这样不论该类内部怎样发生变化,都不会改变序列号。

2.transient:

例如对象在网络中传输也需要实现序列化,但如果希望某个元素不在网络中传输以节省流量,即不参与默认的序列化过程,可将其修饰为:

private transient int age;

这样Student类对象的年龄属性就不会被存储,从而实现了不参与序列化,最终返回的是该属性的默认值。
  但不参与虚拟机默认的序列化不代表不能被序列化,可以手动实现参与序列化过程,将readObject()方法和writeObject()方法的方法签名添加到对应类中,代码如下:

class Student implements Serializable {
    private String name;//可以默认进行序列化
    private transient int age;//需要手动实现序列化

    public Student() {

    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    //添加writeObject()的方法签名
     private void writeObject(java.io.ObjectOutputStream s)throws java.io.IOException{
         s.defaultWriteObject();//将可以默认实现序列化的元素进行序列化操作
         s.writeObject(age);//手动实现age的序列化
     }

    //添加readObject()的方法签名
     private void readObject(java.io.ObjectInputStream s)throws java.io.IOException,ClassNotFoundException{
          s.defaultReadObject();//将可以默认实现反序列化的元素进行反序列化操作
          this.age = (int) s.readObject();//手动实现age的反序列化操作
    }
}

实现手动控制某些元素是否参与序列化过程,目的是为了提高程序运行的性能,可以参考ArrayList的原码,在其定义的数组上修饰了transient,并不是不进行序列化,而是手动实现对有效数据进行序列化,反序列化同理,因此提高了程序的性能。


版权声明:欢迎转载,欢迎扩散,但转载时请标明作者以及原文出处,谢谢合作!             ↓↓↓
上一篇 下一篇

猜你喜欢

热点阅读