Java序列化serializable

序列化:序列化是将对象转成字节的过程
反序列化:反序列化就是将字节还原为对象的过程

需要序列化的原因:

1)持久化:当我们程序创建一个对象的时候,这个对象的生命周期在程序运行结束,或者线程执行完毕之后,就可能被JVM直接回收,被销毁掉。而当我们希望说某个对象在内存中的某个状态被保留下来,这个时候就需要把当前这个对象序列化成二进制字节,然后保存在硬盘里,当需要用的时候,再反序列化还原这个对象即可。例如:当一个程序并发量非常大,需要创建几十万个对象,而对象保存在内存中,一般我们服务器内存不会那么大,或者说在一个内存不足的情况下,就会出现内存溢出,而如果把这些对象暂存在硬盘里面,则就可以缓解内存的压力。

2)传递信息:当处于不同网络中的进程或者服务在进行消息传递的时候,进程间通信是不认识像Java这种对象的,需要把对象序列化成字节,这样计算机才能识别,然后A进程将对象序列化成字节,B进程收到后,再将这些字节还原回对象,则就需要反序列化。一般这种场景在rpc调用非常常见。

例子

序列化需要实现Serializable接口,当调用ObjectOutPutStream的writeObject()方法就可以将一个Object序列化成字节,而相对应的ObjectInPutStream的readObject()方法则可以将二进制字节反序列化为一个对象

public class Student implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;
    transient private String sex;

    public Student(String name, int age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }
}
public class Main {

    public static void main(String [] args) throws Exception {
        Student student = new Student("yushengfan", 20, "男");
        FileOutputStream fileOutputStream = new FileOutputStream("student.txt");
        ObjectOutputStream outputStream = new ObjectOutputStream(fileOutputStream);
        // 此处将student对象序列化,并且将结果保存在student.txt里面
        outputStream.writeObject(student);
        outputStream.close();

        ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("student.txt"));
        // 此处从student.txt读取字节信息,并且反序列化为对象
        Student student1 = (Student) inputStream.readObject();
        System.out.println(student1);
    }
}

输出:

Student{name='yushengfan', age=20, sex='null'}

Process finished with exit code 0

可以看到,我们新建的时候,sex是初始化为"男",但是反序列化回来的时候,结果却是null,原因是由于上面的sex字段是用transient修饰,该修饰是代表,序列化的时候,屏蔽对该变量序列化。如果把transient修饰去掉,实现Serializable接口的时候,则会采取默认策略,即该对象的所有成员都序列化。
如:

public class Student implements Serializable {

    private static final long serialVersionUID = 1L;

    private String name;
    private int age;
    private String sex;

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }
}

运行:

public class Main {

    public static void main(String [] args) throws Exception {
        Student student = new Student("yushengfan", 20, "男");
        FileOutputStream fileOutputStream = new FileOutputStream("student.txt");
        ObjectOutputStream outputStream = new ObjectOutputStream(fileOutputStream);
        // 此处将student对象序列化,并且将结果保存在student.txt里面
        outputStream.writeObject(student);
        outputStream.close();

        ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("student.txt"));
        // 此处从student.txt读取字节信息,并且反序列化为对象
        Student student1 = (Student) inputStream.readObject();
        System.out.println(student1);
    }
}

结果为:

Student{name='yushengfan', age=20, sex='男'}

Process finished with exit code 0

自定义序列化

上面的例子是实现Serialization接口,该接口就是采取默认策略,将所有非transient修饰的成员变量全都序列化,而官方也为我们提供了自定义序列化的策略,就是实现Externalizable接口

public class Student implements Externalizable {
    private String name;
    private int age;
    private String sex;
    // 实现Externalizable 接口的时候,一定需要默认构造函数,否则会报错
    public Student() {}
    public Student(String name, int age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        // 此处只对成员变量name和age进行序列化
        out.writeObject(name);
        out.write(age);
    }
    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        // 此处只对name和age属性进行反序列化
        this.name = (String) in.readObject();
        this.age = in.read();
    }
}

运行:

public class Main {

    public static void main(String [] args) throws Exception {
        Student student = new Student("yushengfan", 20, "男");
        FileOutputStream fileOutputStream = new FileOutputStream("student.txt");
        ObjectOutputStream outputStream = new ObjectOutputStream(fileOutputStream);
        outputStream.writeObject(student);
        outputStream.close();

        ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("student.txt"));
        Student student1 = (Student) inputStream.readObject();
        System.out.println(student1);
    }
}

结果:

Student{name='yushengfan', age=20, sex='null'}

Process finished with exit code 0

此处可以看到,虽然没有用transient修饰变量,但是通过自定义序列化,同意也可以实现transient的功能;

你可能感兴趣的:(Java序列化serializable)