对象序列化

来源:互联网 发布:sql通用查询器 编辑:程序博客网 时间:2024/05/09 01:17

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:mylittleponyrecovering object at Sat Oct 14 11:04:40 CST 2017logon 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()方法。