对象序列化

1.说明

面向实现了Serialization接口的对象,可将它们转换成一系列字节,并可在以后完全恢复原来的样子。换句话说,可以在Windows机器上创建一个对象,通过序列化机制,将其通过网络发送给Linux机器,在通过反序列化可将其恢复成原来的样子。

可实现有限持久化—通过序列化机制,转换成字节流写入到磁盘,在需要的时候再取出来。(有限的原因在于它不像存到数据库中的数据,序列化机制需要自己明确指定序列化和组装对象)

2.序列化过程:

  • 1.创建某些OutputStream对象
  • 2.创建ObjectOutputStream对象,将某些OutputStream对象封装到ObjectOutputStream对象中
  • 3.调用writeObject(object)完成对象的序列化

3.反序列化过程:

  • 1.创建某些InputStream对象
  • 2.创建ObjectInputStream对象,将InputStream对象封装到ObjectInputStream的对象中
  • 3.调用readObject()完成反序列化

定义需要序列化的类

package com.zd.java.io.newio.serialization;
import java.io.Serializable;
/**
 * 寻找类
 * 序列化一个对象,通过网络将其作为文件传送给另一台机器,
 * 另一台机器只更根据文件目录来重新构造此对象
 * Created by ZD on 2017/10/12.
 */
public class Alien implements Serializable{
}

序列化对象

package com.zd.java.io.newio.serialization;
import java.io.*;
/**
 * 创建和序列化Alien对象
 * Created by ZD on 2017/10/12.
 */
public class FreezeAlien {
    public static void main(String[] args) throws IOException {
            ObjectOutput out = new ObjectOutputStream(new FileOutputStream("file.x"));
            Alien zorcon = new Alien();
            out.writeObject(zorcon);
    }
}

反序列化

package com.zd.java.io.newio.serialization;
import java.io.*;
/**
 * 恢复一个序列化的对象,如果想对其做更多的事,必须保证JVM能在本地类路径或者
 * 因特网的其他什么地方找到相关的class文件
 * Created by ZD on 2017/10/12.
 */
public class ThawAlien {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("file.x"));
        Object mystery = in.readObject();
        System.out.println(mystery.getClass().toString());
    }
}

4.序列化的控制:

只序列化对象的一部分,其他部分不序列化,因为对象恢复后,那一部分需要重新构建。下面列出几种方式

4.1ExternalSerializable

在Serializable的基础上扩展了,新增方法writeExternal()和readExternal(),在序列化和重新装配过程中会优先调用这俩个方法。

package com.zd.java.io.newio.serialization;

import java.io.*;

/**
 *保存和恢复一个Externalizable对象必须要做的全部事情
 * 恢复对象时会调用默认构造函数,在调用readExternal方法。保存对象会调用writeExternal方法
 * Created by ZD on 2017/10/12.
 */
public class Blip3 implements Externalizable{

    int i;
    String s;

    public Blip3(){
        System.out.println("Blip3 constructor");
    }
    public Blip3(String x,int a){
        System.out.println("Blip3(String z,int a)");
        s = x;
        i = a;
    }
    public String toString(){
        return s + i;
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        System.out.println("blip3.writeExternal");
        out.writeObject(s);
        out.writeInt(i);//写入对象的重要数据
    }
    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        System.out.println("Blip3.readExternal");
        s = (String) in.readObject();
        i = in.readInt();//恢复数据
    }

    public static void main(String[] args){
        System.out.println("constructing objects:");
        Blip3 blip3 = new Blip3("a string",47);
        System.out.println(blip3.toString());
        try {
            ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("Blip3.out"));
            System.out.println("saving object");
            o.writeObject(blip3);
            o.close();

            ObjectInputStream in = new ObjectInputStream(new FileInputStream("Blip3.out"));
            System.out.println("recovering b3:");
            blip3 = (Blip3) in.readObject();
            System.out.println(blip3.toString());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

说明:使用ExternalSerializable,重新组装对象需要调用默认构建函数,再调用readExternal()方法,序列化对象时会调用writeExternal()方法,在这俩个方法中均需要对重要数据进行操作。如果不进行操作,对象成员数据为成默认数据,操作可能会出现问题。

4.2transient(临时)关键字:

对象中如果存在敏感部分,比如登录中的密码,我们不希望它在序列化过程中被保存下来,因为可能会被别人盗取。一种办法是采用ExternalSerializable,只有在writeObject()中明确序列化,否则不保存。另一种办法采用transient关键字。

做法:实现Serializable接口,将不想序列化的字段设置成transient

package com.zd.java.io.newio.serialization;
import java.io.*;
import java.util.Date;
/**
 * 使用transient进行序列化控制
 * 除密码外其他会话信息需要保存下来
 * Created by ZD on 2017/10/12.
 */
public class Logon implements Serializable{

    private Date date = new Date();
    private String username;
    private transient String password;//transient不会被保存到磁盘

    Logon(String name,String pwd){
        username = name;
        password = pwd;
    }
    public String toString(){
        String pwd = (password == null)?"(n/a)":password;
        return "logon info: \n"+
                "username:"+username+
                "\n date:"+date.toString()+
                "\n password:"+pwd;
    }

    public static void main(String[] args){
        Logon a = new Logon("hulk","mylittlepony");
        System.out.println("logon a = "+a);
        try {
            ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("Logon.out"));
            o.writeObject(a);
            o.close();

            //delay
            int seconds = 5;
            long t = System.currentTimeMillis()+seconds*1000;
            while (System.currentTimeMillis() < t);

            ObjectInputStream in = new ObjectInputStream(new FileInputStream("Logon.out"));
            System.out.println("recovering object at "+new Date());
            a = (Logon) in.readObject();
            System.out.println("logon a = "+a);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
执行结果:
logon a = logon info: 
username:hulk
 date:Sat Oct 14 11:04:34 CST 2017
 password:mylittlepony
recovering object at Sat Oct 14 11:04:40 CST 2017
logon a = logon info: 
username:hulk
 date:Sat Oct 14 11:04:34 CST 2017
 password:(n/a)

4.3ExternalSerializable替代方法:

可以实现Serializable接口,并在类中添加俩个方法:writeObject()、readObject(),方法需要有正确的签名:
private void writeObject(ObjectOutputStream stream)throws IOException;
private void readObject(ObjectIutputStream stream)throws IOException;

package com.zd.java.io.newio.serialization;
import java.io.*;
/**
 * 使用Serializable实现Externalizable的功能
 * 不管是否有transient关键字,优先使用writeObject、readObject
 * Created by ZD on 2017/10/12.
 */
public class SerialCtl implements Serializable {
    String a;
    transient String b;
    public SerialCtl(String aa,String bb){
        a = "not transient:"+aa;
        b = "transient"+bb;
    }
    public String toString(){
        return a + "\n" +b;
    }
    private void writeObject(ObjectOutputStream stream) throws IOException {
        stream.defaultWriteObject();//必须调用
        stream.writeObject(b);
    }
    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
        stream.defaultReadObject();
        b = (String) stream.readObject();
    }
    public static void main(String[] args){
        SerialCtl sc = new SerialCtl("test1","test2");
        System.out.println("before:\n"+sc);
        ByteArrayOutputStream buf = new ByteArrayOutputStream();
        try {
            ObjectOutputStream o = new ObjectOutputStream(buf);
            o.writeObject(sc);//必须检查sc,判断它是否有自己的writeObject()方法
            ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buf.toByteArray()));
            SerialCtl sc2 = (SerialCtl) in.readObject();
            System.out.println("a filter:\n"+sc2);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

5.总结:

序列化方案:

  • 1.实现Serializable接口,自动序列化writeObject(Object object)方法中定义的object,重新组装时是从InputStream中读入的字节进行重新组装对象

  • 2.实现ExternalSerializable接口,在writeExternal()方法中指定需要进行序列化的字段,在 readExternal()方法中指定需要重新组装的字段,组装对象时,调用的是默认构造函数。

  • 3.transient关键字,实现Serializable接口,对不需要序列化的字段声明为transient

  • 4.使用Serializable替代ExternalSerializable接口,在类中添加readObject()、writeObject()方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值