序列化Serializable & Externalizable,附送:Parcelable

                                     Java序列化Serializable & Externalizable

  当需要将对象本地化存储而非仅内存,或者将对象以及相应信息转换为字节序列写入I/O流进行网络传输,然后通过反序列化提取对象及其相关信息而避免重复设置值。Java提供接口为:Serializable 和 Externalizable。

Serializable:

1.基本用法:(基于JDK1.8) 


//需要序列化的实体类只需实现接口即可
public class Person implements Serializable {
    private final static long serialVersionUID = 1L;
    private String name;//姓名
    private String psw;//密码

    public Person(){}

    public Person(String n,String p){
        this.name = n;
        this.psw = p;
    }
    public String getName(){
        return name;
    }
    public String getPsw(){
        return psw;
    }
}

//测试代码
//序列化对象,执行之后在Idea的src文件夹下出现person.obj文件
    private static void writeObjByS(){//static非必须
        Person p = new Person("chen","1234");
        try {
            ObjectOutputStream objOutStream = new ObjectOutputStream(
                                  new FileOutputStream("src/person.obj"));
            objOutStream.writeObject(p);
            objOutStream.flush();
            objOutStream.close();//务必执行
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
//反序列化对象
    private static void readObjByS(){
        Person p;
        try {
            ObjectInputStream objStream = new ObjectInputStream(
                                  new FileInputStream("src/person.obj"));
            p = (Person)objStream.readObject();
            objStream.close();//务必执行
            System.out.println("log the message is --> name:"  
                                + p.getName() + " psw = " +p.getPsw());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
//打印结果:log the message is --> name:chen psw = 1234

2.更多细节

 (1) serialVersionUID :

     首先是声明问题,在接口中是这样定义的:

If a serializable class does not explicitly declare a serialVersionUID, 

then the serialization runtime will calculate a default serialVersionUID value

 for that class based on various aspects of the class    ---Java

也就是说你不显示声明的时候系统会默认创建,那到底有啥作用哦?

   举个例子:在上文的序列化写入文件之后,反序列化之前,修改本地serialVersionUID的值(与序列化时不一致),果然抛出异常,反序列化失败:

     所以说作用是在反序列化的时候,将字节序列中的serialVersionUID与本地中的实体类中的serialVersionUID对比,不一致的话就会抛出:

       java.io.InvalidClassException;local class incompatible 等异常信息!

 但是还是推荐设置UID值,方便升级。

(2)transient:使用transient修饰的成员变量不会被序列化(避免了敏感信息在网络传输中泄漏的风险,比如password)。读者可自行验证呀!

(3)static:static修饰的变量不会被序列化,例子如下:


//在Person添加如下字段 (打印字段也添加了)
private static int age;//注意这个地方可没有赋值
public void setAge(int newAge){
   age = newAge;
   System.out.println("change age to :" + newAge);
}
public int getAge(){  return age;}


//序列化之前,设置static age = 66;
Person p = new Person("chen","1234");
p.setAge(66);

最终,打印age值为0,但我在序列化之前设置age值为66了呀?

 再试着将age初始值设置为22,其余一切如旧,结果打印的是初始值22而非66。可见对于所有实例对象共享的静态变量,有一说一,确实不能被序列化,读取的也只是默认值(或者初始值)而已

Externalizable:

 1.基本用法

public class DogE implements Externalizable {
    private final static long serialVersionUID = 1L;
    private String address;
    private int age;
    private int sons;

    public DogE(){}//这里无参构造函数是必须的
    public DogE(String add,int age,int sons){
        this.address = add;
        this.age = age;
        this.sons = sons;
    }
    public String getAddress() { return address; }
    public int getAge() { return age; }
    public int getSons() { return sons; }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeUTF(address);
        out.writeInt(age);
        out.writeInt(sons);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        this.address = in.readUTF();
        this.age = in.readInt();
        this.sons = in.readInt();   
    }
}


//测试代码 序列化
    private static void writeByEZ(){
        DogE dogE = new DogE("m",22,2);
        ObjectOutputStream objOStream;
        try {
            objOStream = new ObjectOutputStream(new FileOutputStream("src/doge.txt"));
            dogE.writeExternal(objOStream);
            objOStream.flush();
            objOStream.close();//这里务必执行,不然read时候可能会有EOF异常
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
//反序列化
    private static void readByEZ(){
        DogE d = new DogE();
        ObjectInputStream objInStream;
        try {
            objInStream = new ObjectInputStream(new FileInputStream("src/doge.txt"));
            d.readExternal(objInStream);
            objInStream.close();
            System.out.println("log msg: address --> " +
                                d.getAddress() + " age:" +d.getAge() + " sons:" + d.getSons());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

FBI WARNING:实体类中变量的写入顺序和读出顺序应该一致!


//顺序错但是类型没有出错时也会顺利读出,但是age值赋给了sons,注意别出差哦
 //写
 out.writeInt(age);
 out.writeInt(sons);
 //读 
 this.sons = in.readInt();
 this.age = in.readInt();

总结:

    相较于序列化方式,Serializable对整个类和信息进行序列化,而Externalizable则可以选择需要的序列化对象。

附带:

Android中使用Intent进行activity之间传递信息,使用的序列化为:Parcelable

实例:

public class LoginInfo implements Parcelable {
    private String name;
    private int    age;
    private String psw;

    private LoginInfo(Parcel in) {
        this.name = in.readString();
        this.age  = in.readInt();
        this.psw  = in.readString();
    }

    public LoginInfo(String name,int age,String psw){
        this.name = name;
        this.age  = age;
        this.psw  = psw;
    }

    @NonNull
    @Override
    public String toString() {
        return "name --> " + name +" ;age --> " + age +" ;psw --> "+ psw;
    }

    @Override
    public int describeContents() {//返回默认值即可
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(age);
        dest.writeString(psw);
    }
    //底层调用
    public static final Creator<LoginInfo> CREATOR = new Creator<LoginInfo>() {
        @Override
        public LoginInfo createFromParcel(Parcel in) {
            return new LoginInfo(in);
        }

        @Override
        public LoginInfo[] newArray(int size) {
            return new LoginInfo[size];
        }
    };
}

Intent使用:

//启动
LoginInfo info = new LoginInfo("cheng",22,"1234");
Bundle bundle = new Bundle();
bundle.putParcelable("login_info",info);
Intent intent = new Intent(this,IntentReceiverActivity.class);
intent.putExtras(bundle); //可简写为:intent.putExtra("login_info",info)
startActivity(intent);

//接收
Intent intent = getIntent();
LoginInfo info = intent.getParcelableExtra("login_info");

注:当向另一应用使用Intent发送数据时,请勿使用Parcelable或者Serializable,对方无完全相同的无序列化类,则会抛出RuntimeException

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值