Externalizable和Serializable

本文详细介绍了Java中对象序列化的两种方式:Serializable和Externalizable。对比了这两种方式的特点,并通过实例演示了如何自定义序列化过程,特别是如何处理transient关键字修饰的变量。
摘要由CSDN通过智能技术生成

1、Serializable序列化时不会调用默认的构造器,而Externalizable序列化时会调用默认构造器的!!!
2、Serializable:一个对象想要被序列化,那么它的类就要实现 此接口,这个对象的所有属性(包括private属性、包括其引用的对象)都可以被序列化和反序列化来保存、传递。
Externalizable:他是Serializable接口的子类,有时我们不希望序列化那么多,可以使用这个接口,这个接口的writeExternal()和readExternal()方法可以指定序列化哪些属性。

注意:
对象的序列化并不属于新的Reader和Writer层次结构的一部分,而是沿用老式的InputStream和OutputStream结构,在某些情况下,不得不混合使用两种类型的层次结构。

恢复了一个反序列化的对象后,如果想对其做更多的事情(对象.getClass().xxx),必须保证JVM能在本地类路径或者因特网的其他什么地方找到相关的.class文件。
恢复对象的默认构建器必须是public的,否则会抛异常。

由于Externalizable对象默认时不保存对象的任何字段,所以transient关键字只能伴随Serializable使用,虽然Externalizable对象中使用transient关键字也不报错,但不起任何作用。
3、把transient修饰的字段序列化。这也可以?of course!!

import java.io.*;
import java.util.*;

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 o)throws IOException{
                o.defaultWriteObject();
                o.writeObject(b);
        }
        private void readObject(ObjectInputStream streamr)throws IOException, ClassNotFoundException{
                streamr.defaultReadObject();
                b=(String)streamr.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 out1 = new
                                ObjectOutputStream(buf);

                        out1.writeObject(sc);

                        ObjectInputStream in1 = new
                                ObjectInputStream(new
                                        ByteArrayInputStream(buf.toByteArray()));

                        SerialCtl sc2 = (SerialCtl)in1.readObject();
                        System.out.println("After:\n"+sc2);

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

                }
        }
}

我们看下输出结果就知道了:这里写图片描述
虽然 String b被transient修饰了,但是b仍然被序列化了。
假如代码稍作修改,transient又起作用了

import java.io.*;
import java.util.*;

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 o)throws IOException{
                o.defaultWriteObject();
        //      o.writeObject(b);
        }
        private void readObject(ObjectInputStream streamr)throws IOException, ClassNotFoundException{
                streamr.defaultReadObject();
        //      b=(String)streamr.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 out1 = new
                                ObjectOutputStream(buf);

                        out1.writeObject(sc);

                        ObjectInputStream in1 = new
                                ObjectInputStream(new
                                        ByteArrayInputStream(buf.toByteArray()));

                        SerialCtl sc2 = (SerialCtl)in1.readObject();
                        System.out.println("After:\n"+sc2);

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

                }
        }
}

结果:这里写图片描述
方法writeObject处理对象的序列化。如果声明该方法,它将会被ObjectOutputStream调用而不是默认的序列化进程。如果你是第一次看见它,你会很惊奇尽管它们被外部类调用但事实上这是两个private的方法。并且它们既不存在于java.lang.Object,也没有在Serializable中声明。那么ObjectOutputStream如何使用它们的呢?这个吗,ObjectOutputStream使用了反射来寻找是否声明了这两个方法。因为ObjectOutputStream使用getPrivateMethod(通过getDeclareMethod可以获得私有方法),所以这些方法不得不被声明为private以至于供ObjectOutputStream来使用。
在两个方法的开始处,你会发现调用了defaultWriteObject()和defaultReadObject()。它们做的是默认的序列化进程,就像写/读所有的non-transient和 non-static字段(但他们不会去做serialVersionUID的检查).通常说来,所有我们想要自己处理的字段都应该声明为transient。这样的话,defaultWriteObject/defaultReadObject便可以专注于其余字段,而我们则可为这些特定的字段(译者:指transient)定制序列化。使用那两个默认的方法并不是强制的,而是给予了处理复杂应用时更多的灵活性。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值