transient关键字与序列化、反序列化

transient,中文翻译是短暂的,和对象序列化、反序列化有关。

一个类只要实现了Serializable接口,则该类实例就可以序列化,具体来说实例的每个非静态成员变量都会序列化。注意是非静态成员变量,静态成员变量不会序列化。但是假如某些字段涉及敏感信息,不能序列化存储到文件中或者经网络传输,则就应该用transient修饰。用transient修饰的变量在对象序列化时默认不会序列化,同时反序列化得到的对象中该变量值会是对应类型的默认值。

代码示例:

Address类:

public class Address implements Serializable {
    private String nation;
    private String city;
    private String buildingNo;

    public String getNation() {
        return nation;
    }

    public void setNation(String nation) {
        this.nation = nation;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getBuildingNo() {
        return buildingNo;
    }

    public void setBuildingNo(String buildingNo) {
        this.buildingNo = buildingNo;
    }

    public Address() {

    }

    public Address(String nation, String city, String buildingNo) {
        this.nation = nation;
        this.city = city;
        this.buildingNo = buildingNo;
    }

    @Override
    public String toString() {
        return "Address{" +
                "nation='" + nation + '\'' +
                ", city='" + city + '\'' +
                ", buildingNo='" + buildingNo + '\'' +
                '}';
    }
}

Person类:

public class Person implements Serializable {
    private String name;
    private Integer age;
    private Address address;

    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 Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }


    public Person(String name, Integer age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address=" + address +
                '}';
    }
}

测试类Test:

public class Test {
    public static void main(String[] args) {
        String path = "d:/s.txt";
        try (OutputStream output = new FileOutputStream(path);
             ObjectOutputStream oos = new ObjectOutputStream(output);
             InputStream input = new FileInputStream(path);
             ObjectInputStream ois = new ObjectInputStream(input)
        ) {
            oos.writeObject(new Person("zhangsan", 18, new Address("China", "Shenzhen", "FuTian")));
            Person person = (Person) ois.readObject();
            System.out.println(person);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

注意,上面说的是用transient修饰的变量在对象序列化时默认不会序列化,同时反序列化得到的对象中该变量值会是对应类型的默认值。注意,是默认,不是一定。因为是有办法让transient修饰的变量也参与序列化的。只需在类中加入两个方法即可:

void writeObject(ObjectOutputStream oos) throws IOException;

void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException;

其中第一个是序列化方法,可以指定序列化的成员变量,第二个是反序列化方法,可以指定反序列化的成员变量。可以在这两个方法中显式指定被transient修饰的变量,这样该变量同样可以参与序列化和反序列化。

代码示例:

其他类代码不变,Person类:

public class Person implements Serializable {
    private String name;
    private Integer age;
    private transient Address address;

    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 Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public Person(String name, Integer age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address=" + address +
                '}';
    }

    private void writeObject(ObjectOutputStream oos) throws IOException {
        oos.defaultWriteObject();
        oos.writeObject(address);
        System.out.println("person serialized");
    }

    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        address = (Address) ois.readObject();
        System.out.println("person deserialized");
    }
}

上面代码中,标红的oos.defaultWriteObject(); 和 ois.defaultReadObject(); 是调用默认的序列化方法和反序列化方法去操作没有transient修饰的非静态成员变量(default方法能且仅能操作non-static、non-transient变量),之后用oos.writeObject(address); 和 address = (Address) ois.readObject(); 来显式指定address变量。这样,执行完Test类的main方法之后,同样可以打印出adderss信息,即使address用transient修饰了。

Serializable接口还有个Externalizable子接口。该接口有2个方法:

void writeExternal(ObjectOutput out) throws IOException;

void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;

如果自定义类实现了Externalizable接口,则transient在该类中不起任何作用,属性能不能序列化和有没有transient修饰没有半毛钱关系。如果想序列化该类实例的某个属性,必须在writeExternal()方法中显式write,如果想反序列化时能够获取到属性值,必须在readExternal()显式read,且各属性write和read的顺序必须一样。如果writeExternal()方法和readExternal()方法中没有具体实现,则任何属性都不会序列化。

代码示例:

其他类不变,Person类:

public class Person implements Externalizable {
    private String name;
    private Integer age;
    private transient Address address;

    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 Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public Person() {
    }

    public Person(String name, Integer age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address=" + address +
                '}';
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(name);
        out.writeObject(age);
        out.writeObject(address);
        System.out.println("person serialized");
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        name = (String) in.readObject();
        age = (Integer) in.readObject();
        address = (Address) in.readObject();
        System.out.println("person deserialized");
    }
}

需要指出的是,假如想要通过反序列化来得到Externalizable实例,则Externalizable接口实现类必须要有无参构造器,否则会报java.io.InvalidClassException异常,因为在反序列化过程中,是先通过无参构造器创造出对象,然后调用各属性的setter给属性设置值的。

同时需要指出的是,假如一个类没有实现Serializable接口或者Externalizable接口,则在序列化该类实例时会报java.io.NotSerializableException异常。更进一步,假如该类没有被transient修饰的非静态成员变量所属类型没有实现Serializable接口或者Externalizable接口,则在序列化时也会报java.io.NotSerializableException异常。

转载于:https://www.cnblogs.com/koushr/p/5873397.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值