序列化的理解与应用

序列化的理解与应用

1. 什么是序列化
如果我们需要在网络上传输文件,我们不可能直接传输给对方;有人会问:为什么我在本机上可以直接存储数据
列如:我们需要操作数据库,我们是把数据直接储存在数据库里面,并没有进行序列化;在这里需要说明一点是:
在本机上储存数据,我们是直接通过计算服务与计算机的内存打交道;
但是,不同电脑通过同一个网络进行连接传输数据时,是没有办法直接传输数据的,网络之间传输数据,是通过编码
进行传输的,因此就需要使用序列化;
2. 实列化与没有实例化
列如:
(1)未实列化时,person是保存到堆中的一个实例;

    1.Person  person = new Person();  
    2.person.setWidth(37);
(2)实例化:通过实例化,我们可以把person会被保存到一个.ser文件里面
  1.FileOutPutStream out = new FieOutPutStream("p.ser");
  2.ObjectOutputStream os = new ObjectOutputStream(out); 
  3. os.write(person);
  4. os.close();

3.如何进行实例化
第一步:创建文件输出流:FileOutPutStream out = new FieOutPutStream();
第二步:创建对象输出流:ObjectOutStream os = new ObjectOutPutStream()
第三步:调用输出流的write()方法;
第四步:关闭流:close()
4.需要注意的事项
1)序列化时,只对对象的状态进行保存,而不管对象的方法;
2)当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口;
3)当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化;
4)并非所有的对象都可以序列化,,至于为什么不可以,有很多原因了,比如:
1.安全方面的原因,比如一个对象拥有private,public等field,对于一个要传输的对象,比如写到文件,或者进行rmi传输 等等,在序列化进行传输的过程中,这个对象的private等域是不受保护的。
5.java序列化的过程
Java平台允许我们在内存中创建可复用的Java对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在,即,这些对象的生命周期不会比JVM的生命周期更长。但在现实应用中,就可能要求在JVM停止运行之后能够保存(持久化)指定的对象,并在将来重新读取被保存的对象。Java对象序列化就能够帮助我们实现该功能。
使用Java对象序列化,在保存对象时,会把其状态保存为一组字节,在未来,再将这些字节组装成对象。必须注意地是,对象序列化保存的是对象的"状态",即它的成员变量。由此可知,对象序列化不会关注类中的静态变量。
除了在持久化对象时会用到对象序列化之外,当使用RMI(远程方法调用),或在网络中传递对象时,都会用到对象序列化。
实列:
1)定义一个枚举类型(枚举类型可以被序列化):

public enum Gender {
    MALE, FEMALE
}

2)定义一个person类;

public class Person implements Serializable {

    private String name = null;

    private Integer age = null;

    private Gender gender = null;

    public Person() {
        System.out.println("none-arg constructor");
    }

    public Person(String name, Integer age, Gender gender) {
        System.out.println("arg constructor");
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Gender getGender() {
        return gender;
    }

    public void setGender(Gender gender) {
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "[" + name + ", " + age + ", " + gender + "]";
    }
}

3)定义一个main()方法实现序列化

public class SimpleSerial {

    public static void main(String[] args) throws Exception {
        File file = new File("person.out");

        ObjectOutputStream oout = new ObjectOutputStream(new FileOutputStream(file));
        Person person = new Person("John", 101, Gender.MALE);
        oout.writeObject(person);
        oout.close();

        ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));
        Object newPerson = oin.readObject(); // 没有强制转换到Person类型
        oin.close();
        System.out.println(newPerson);
    }
}
   /*上述程序的输出的结果为:

arg constructor
[John, 31, MALE]
  此时必须注意的是,当重新读取被保存的Person对象时,并没有调用Person的任何构造器,看起来就像是直接使用字节将Person对象还原出来的。
当Person对象被保存到person.out文件中之后,我们可以在其它地方去读取该文件以还原对象,但必须确保该读取程序的CLASSPATH中包含有Person.class(哪怕在读取Person对象时并没有显示地使用Person类,如上例所示),否则会抛出ClassNotFoundException。*/

6.与Serializable有关的关键字以及方法

  1. transient关键字
    当某个字段被声明为transient后,默认序列化机制就会忽略该字段。此处将Person类中的age字段声明为transient,如下所示,
public class Person implements Serializable {    
    transient private Integer age = null;   
}

再执行SimpleSerial应用程序,会有如下输出:
arg constructor
[John, null, MALE]
可见,age字段未被序列化。
2. writeObject()方法与readObject()方法
对于上述已被声明为transitive的字段age,除了将transitive关键字去掉之外,是否还有其它方法能使它再次可被序列化?方法之一就是在Person类中添加两个方法:writeObject()与readObject(),如下所示:

public class Person implements Serializable {    
    
      transient private Integer age = null;
       private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        out.writeInt(age);
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        age = in.readInt();
    }
}

在writeObject()方法中会先调用ObjectOutputStream中的defaultWriteObject()方法,该方法会执行默认的序列化机制,此时会忽略掉age字段。然后再调用writeInt()方法显示地将age字段写入到ObjectOutputStream中。readObject()的作用则是针对对象的读取,其原理与writeObject()方法相同。
再次执行SimpleSerial应用程序,则又会有如下输出:

arg constructor [John, 31, MALE]
必须注意地是,writeObject()与readObject()都是private方法,那么它们是如何被调用的呢?毫无疑问,是使用反射。详情可见ObjectOutputStream中的writeSerialData方法,以及ObjectInputStream中的readSerialData方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值