Java对象序列化和反序列化

Java对象序列化和反序列化

在Java中,我们如果要保存一个对象的瞬时状态值,以便在下次使用时能够得到这些值,或者持久化对象,或者使用RMI(远程方法调用),或在网络中传递对象时,此时我们就需要将对象序列化,实现序列化,我们只要实现Serializable接口,该接口是一个标记接口(Tag interface),即里面没有方法,其主要作用就是告诉JVM该类的对象可以进行序列化。

一般来说,很多类的对象都实现了Serializable接口,但是,有些对象是不能进行序列化的,比如与数据库相关的连接对象,file对象等等,保存这些对象的状态值是没有意义的,因此Object并没有实现Serializable接口也是这个原因,若想要将对象序列化,我们只要实现Serializable接口即可,一个类的父类实现了Serializable接口,则其子类默认也会实现该接口,反过来,若其子类需要序列化,则其父类不一定要实现Serializable接口。

package test2;

/**
 * Created by siege on 2015-09-05.
 */
public class Creature {
    public Creature() {
        System.out.println("Creature");
    }
}

package test2;


/**
 * Created by siege on 2015-09-05.
 */
public class Person extends Creature {
    private String name;
    private int age;
    private String sex;

    public Person(String name, int age, String sex) {
        System.out.println("Person父类有参构造器,"+"name:"+name+",age:"+age+",sex:"+sex);
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    public Person() {
        System.out.println("Person父类无参构造器");
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public String getSex() {
        return sex;
    }
}

package test2;

import java.io.Serializable;

/**
 * Created by siege on 2015-09-05.
 */
public class Student extends Person implements Serializable {
    private float score;
    private int grade;

    public Student(float score, int grade) {
        super("siege", 25, "male");
        System.out.println("Student子类构造器,"+"score:"+score+",grade:"+grade);
        this.score = score;
        this.grade = grade;
    }

    public float getScore() {
        return score;
    }

    public int getGrade() {
        return grade;
    }
}

package test2;

import java.io.*;

/**
 * Created by siege on 2015-09-05.
 */
public class TestSerivalizable {
    public static void main(String[] args) {
        FileOutputStream fos;
        ObjectOutputStream oos;
        Student student=new Student(78f,2);
        try {
            fos=new FileOutputStream("C:\\test\\student.out");
            oos=new ObjectOutputStream(fos);
            oos.writeObject(student);

        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

package test2;

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

/**
 * Created by siege on 2015-09-05.
 */
public class TestSerivalizable1 {
    public static void main(String[] args) {

           FileInputStream fio;
           ObjectInputStream ois;
        try {
            fio = new FileInputStream("C:\\test\\student.out");
            ois = new ObjectInputStream(fio);
            Student s = (Student) ois.readObject();
            System.out.println("name:"+s.getName()+",age:"+s.getAge()+",sex:"+s.getSex()
                            +",score:"+s.getScore()+",grade:"+s.getGrade()
            );
        }catch (Exception e){
            e.printStackTrace();
        }



    }
}

在序列化Student时,其打印结果如下:

Creature
Person父类有参构造器,name:siege,age:25,sex:male
Student子类构造器,score:78.0,grade:2

由于Person类没有实现Serializable接口,故在反序列化时调用了其无参构造器,同时调用了其父类Creature的构造器进行了初始化,导致其Persond对象中的状态信息丢失:

Creature
Person父类无参构造器
name:null,age:0,sex:null,score:78.0,grade:2

若Person类实现了Serializable接口,则结果如下:

Creature
name:siege,age:25,sex:male,score:78.0,grade:2

此时只调用了未实现Serializable接口的Creature构造器。同时信息未丢失。

序列化是为了保存对象的状态,因此,static类型的变量不会被序列化,因为保存其值是没有意义的,它会在运行过程中动态变化的,对于引用类型的变量,或者其引用变量中又包含引用变量,我们在序列化该对象时,都对其进行了序列化,我们只要确保他们都实现了Serializable接口。

在序列化的过程中,如果对于有些变量,我们不希望保存其值,那么我们在该引用变量或者原始变量名前加上关键字transient即可,这样,该字段就不会被序列化了,在反序列化恢复成对象时,其默认值为null(字段为对象类型)或者原始类型的默认值(原始类型变量)。

在反序列化时,JVM将序列化的对象进行重新组装,然后在heap中开辟空间,生成相应类型的对象,在该过程中,其并不调用构造器(因为调用构造器则将其值进行了初始化,而不是我们保存时的状态了)。但是,如果父类没有实现Serializable,子类实现了Serializable接口,则在反序列化时会调用父类即其父类以上的构造器。当然,我们我们如果不想使用java提供的默认的序列化方式,我们只要实现Externalizable接口即可,当读取对象时,会调用被序列化类的无参构造器去创建一个新的对象,然后再将被保存对象的字段的值分别填充到新对象中。用该接口时,序列化的细节需要由程序员去完成。

举例如下:

package com.test;

import java.io.Serializable;

/**
 * Created by siege on 2015-07-28.
 */
public class Person implements Serializable {

    private transient  int num=3;
    private transient  String description="hello";
    private static  int count;
    private String name;
    private int age;

    public Person(String name, int age) {
        count++;
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "name"+":"+name+",age:"+age+",count:"+count+",num:"+num+",description:"+description;
    }
}

package com.test;

import java.io.*;

/**
 * Created by siege  on 2015-07-28.
 */
public class SerializableTest {

    public static void main(String[] args) {
        Person person1=new Person("siege1",20);
        Person person2=new Person("siege2",21);
        Person person3=new Person("siege3",22);
        try {
            FileOutputStream fos=new FileOutputStream("C:\\test\\person.out");
            ObjectOutputStream objectOutputStream=new ObjectOutputStream(fos);
            objectOutputStream.writeObject(person1);
            objectOutputStream.writeObject(person2);
            objectOutputStream.writeObject(person3);
            objectOutputStream.close();
        } catch (java.io.IOException e) {
            e.printStackTrace();
        }

        try {
            FileInputStream fin=new FileInputStream("C:\\test\\person.out");
            ObjectInputStream objectInputStream=new ObjectInputStream(fin);
            try {
                Person p1=(Person)objectInputStream.readObject();
                Person p2=(Person)objectInputStream.readObject();
                Person p3=(Person)objectInputStream.readObject();
                System.out.println(p1);
                System.out.println(p2);
                System.out.println(p3);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

输出结果如下:

name:siege1,age:20,count:3,num:0,description:null
name:siege2,age:21,count:3,num:0,description:null
name:siege3,age:22,count:3,num:0,description:null

由此可见,static变量的值是在我们反序列化之后再从类变量中取出放入对象中的,同时,transient类型的变量没有序列化,反序列化的值为默认值。

若Person没有实现Serializable接口,则会出现

java.io.NotSerializableException

错误。

如果我们在序列化对象之后,将该对象对应的类做了改动,则在反序列化时可能出现异常(java.io.InvalidClassException),原因是JVM在序列化对象时会在序列化对象上和其类上生成一个同一个serialVersionUID,在反序列化时,会比较该值,如果相同,则可以序列化,否则出现异常。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值