Java序列化——transient关键字和Externalizable接口

提到Java序列化,相信大家都不陌生。我们在序列化的时候,需要将被序列化的类实现Serializable接口,这样的类在序列化时,会默认将所有的字段都序列化。那么当我们在序列化Java对象时,如果不希望对象中某些字段被序列化(如密码字段),怎么实现呢?看一个例子:

import java.io.Serializable;
import java.util.Date;

public class LoginInfo implements Serializable {
    private static final long serialVersionUID = 8364988832581114038L;
    private String userName;
    private transient String password;//Note this key word "transient"
    private Date loginDate;
    
    //Default Public Constructor
    public LoginInfo() {
        System.out.println("LoginInfo Constructor");
    }
    
    //Non-Default constructor
    public LoginInfo(String username, String password) {
        this.userName = username;
        this.password = password;
        loginDate = new Date();
    }
    public String toString() {
        return "UserName=" + userName + ", Password=" 
                + password + ", LoginDate=" + loginDate;
    }
}

测试类:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Test {
    static String fileName = "C:/x.file";
    public static void main(String[] args) throws Exception {
        LoginInfo info = new LoginInfo("name", "123");
        System.out.println(info);
        //Write
        System.out.println("Serialize object");
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fileName));
        oos.writeObject(info);
        oos.close();
        //Read
        System.out.println("Deserialize object.");
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName));
        LoginInfo info2 = (LoginInfo)ois.readObject();
        ois.close();
        System.out.println(info2);
    }
}

执行结果:

UserName=name, Password=123, LoginDate=Wed Nov 04 16:41:49 CST 2015
Serialize object
Deserialize object.
UserName=name, Password=null, LoginDate=Wed Nov 04 16:41:49 CST 2015

另一种可以达到此目的的方法可能就比较少用了,那就是——不实现Serializable而实现Externalizable接口。这个Externalizable接口有两个方法,分别表示在序列化的时候需要序列化哪些字段和反序列化的时候能够反序列化哪些字段:

void writeExternal(ObjectOutput out) throws IOException;
void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;

于是就有了下面的代码:

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Date;

public class LoginInfo2 implements Externalizable {
    private static final long serialVersionUID = 8364988832581114038L;
    private String userName;
    private String password;
    private Date loginDate;
    
    //Default Public Constructor
    public LoginInfo2() {
        System.out.println("LoginInfo Constructor");
    }
    
    //Non-Default constructor
    public LoginInfo2(String username, String password) {
        this.userName = username;
        this.password = password;
        loginDate = new Date();
    }
    public String toString() {
        return "UserName=" + userName + ", Password=" 
                + password + ", LoginDate=" + loginDate;
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        System.out.println("Externalizable.writeExternal(ObjectOutput out) is called");
        out.writeObject(loginDate);
        out.writeUTF(userName);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        System.out.println("Externalizable.readExternal(ObjectInput in) is called");
        loginDate = (Date)in.readObject();
        userName = (String)in.readUTF();
    }
}

测试类除了类名使用LoginInfo2以外,其他保持不变。下面是执行结果:

UserName=name, Password=123, LoginDate=Wed Nov 04 16:36:39 CST 2015
Serialize object
Externalizable.writeExternal(ObjectOutput out) is called
Deserialize object.
LoginInfo Constructor //-------------------------Note this line
Externalizable.readExternal(ObjectInput in) is called
UserName=name, Password=null, LoginDate=Wed Nov 04 16:36:39 CST 2015

可以看到,反序列化后的Password一项依然为null。

需要注意的是:对于恢复Serializable对象,对象完全以它存储的二进制为基础来构造,而不调用构造器。而对于一个Externalizable对象,public的无参构造器将会被调用(因此你可以看到上面的测试结果中有LoginInfoConstructor这一行),之后再调用readExternal()方法。在Externalizable接口文档中,也给出了相关描述:

When an Externalizable object is reconstructed, an instance is created using the public no-arg constructor, then the readExternal method called. Serializable objects are restored by reading them from an ObjectInputStream.

如果没有发现public的无参构造器,那么将会报错。(把LoginInfo2类的无参构造器注释掉,就会产生错误了):

UserName=name, Password=123, LoginDate=Wed Nov 04 17:03:24 CST 2015
Serialize object
Deserialize object.
Exception in thread "main" java.io.InvalidClassException: LoginInfo2; no valid constructor
	at java.io.ObjectStreamClass$ExceptionInfo.newInvalidClassException(ObjectStreamClass.java:150)
	at java.io.ObjectStreamClass.checkDeserialize(ObjectStreamClass.java:768)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1772)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
	at Test2.main(Test2.java:19)

那么,如果把Externalizable接口和transient关键字一起用,会是什么效果呢?我们在LoginInfo2中的password加上关键字transient,再修改writeExternal()和readExternal()方法:

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(loginDate);
        out.writeUTF(userName);
        out.writeUTF(password);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        loginDate = (Date)in.readObject();
        userName = (String)in.readUTF();
        password = (String)in.readUTF();
    }

执行结果:

UserName=name, Password=123, LoginDate=Wed Nov 04 16:58:27 CST 2015
Serialize object
Deserialize object.
LoginInfo Constructor
UserName=name, Password=123, LoginDate=Wed Nov 04 16:58:27 CST 2015

从结果中可以看到,尽管在password字段上使用了transient关键字,但是这还是没能阻止被序列化。因为不是以Serializable方式去序列化和反序列化的。也就是说:transient关键字只能与Serializable接口搭配使用

时间: 2024-07-31 14:32:35

Java序列化——transient关键字和Externalizable接口的相关文章

Java IO 序列化 transient关键字

Java IO 序列化 transient关键字 @author 敏敏Alexia 转自:http://www.cnblogs.com/lanxuezaipiao/p/3369962.html 1. transient的作用及使用方法 我们都知道一个对象只要实现了Serilizable接口,这个对象就可以被序列化,java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过程,只要这个类实现了Serilizable接口,这个类的所有属性和方法都会自动序列化. 然而在实际开发过程

Java中transient关键字的应用

今天跟JDK源码的时候发现transient ,感觉从来没见过,于是用google查了一下. Java语言的关键字,用来表示一个域不是该对象串行化的一部分.当一个对象被串行化的时候,transient型变量的值不包括在串行化的表示中,然而非transient型的变量是被包括进去的. 下面我们查创建一个LoginFile的类: public class LoginFile implements Serializable{ private static final long serialVersio

java 中 transient 关键字意义

译文出处:Why does Java have transient variables? java 中的 transient 关键字表明了 transient 变量不应该被序列化(transient). 参考Java Language Specification, Java SE 7 Edition, Section 8.3.1.3. transient Fields: 被 transient 标记的这些变量,表明他们是某个对象中不需要被持久状态的部分(Variables may be mark

Java的transient关键字

transient:  adj. 短暂的:路过的:n. 瞬变现象:过往旅客:候鸟 Java语言的关键字,变量修饰符,如果用transient声明一个实例变量,当对象存储时,它的值不需要维持.换句话来说就是,用transient关键字标记的成员变量不参与序列化过程. 原文地址:https://www.cnblogs.com/runwithraining/p/11520775.html

Java Volatile transient 关键字

随笔-204  评论-134  文章-0  trackbacks-0 Volatile修饰的成员变量在每次被线程访问时,都强迫从主内存中重读该成员变量的值.而且,当成员变量发生变化时,强迫线程将变化值回写到主内存.这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值.     Java语言规范中指出:为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比.     这样当多个线程同时与某个对象交互时,就必须要注意到要让线程及

Java的transient关键字(转)

Volatile修饰的成员变量在每次被线程访问时,都强迫从主内存中重读该成员变量的值.而且,当成员变量发生变化时,强迫线程将变化值回写到主内存.这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值.     Java语言规范中指出:为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比.     这样当多个线程同时与某个对象交互时,就必须要注意到要让线程及时的得到共享成员变量的变化.     而volatile关键字就是提示

Java transient关键字序列化时使用小记

1. transient的作用及使用方法 我们都知道一个对象只要实现了Serilizable接口,这个对象就可以被序列化,java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过程,只要这个类实现了Serilizable接口,这个类的所有属性和方法都会自动序列化. 然而在实际开发过程中,我们常常会遇到这样的问题,这个类的有些属性需要序列化,而其他属性不需要被序列化,打个比方,如果一个用户有一些敏感信息(如密码,银行卡号等),为了安全起见,不希望在网络操作(主要涉及到序列化操

java的序列化之Externalizable接口

控制序列化字段还可以使用Externalizable接口替代Serializable借口.此时需要定义一个默认构造器,否则将为得到一个异常(java.io.InvalidClassException: Person; Person; no valid constructor):还需要定义两个方法(writeExternal()和readExternal())来控制要序列化的字段. 在Person类中 package com.kc.iotest02; import java.io.External

Java transient关键字

哎,虽然自己最熟的是Java,但很多Java基础知识都不知道,比如transient关键字以前都没用到过,所以不知道它的作用是什么,今天做笔试题时发现有一题是关于这个的,于是花个时间整理下transient关键字的使用,涨下姿势~~~好了,废话不多说,下面开始: 1. transient的作用及使用方法 我们都知道一个对象只要实现了Serilizable接口,这个对象就可以被序列化,java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过程,只要这个类实现了Seriliza