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

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

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

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;

    }

}

    测试类:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

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);

    }

}

    执行结果:

?


1

2

3

4

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接口有两个方法,分别表示在序列化的时候需要序列化哪些字段和反序列化的时候能够反序列化哪些字段:

?


1

2

void writeExternal(ObjectOutput out) throws IOException;

void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;

    于是就有了下面的代码:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

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以外,其他保持不变。下面是执行结果:

?


1

2

3

4

5

6

7

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类的无参构造器注释掉,就会产生错误了):

?


1

2

3

4

5

6

7

8

9

10

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()方法:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

    @Override

    public void writeExternal(ObjectOutput out) throws IOException {

        out.writeObject(loginDate);

        out.writeUTF(userName);

        out.writeUTF(password);//强行将transient修饰的password属性也序列化进去

    }

 

    @Override

    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

        loginDate = (Date)in.readObject();

        userName = (String)in.readUTF();

        password = (String)in.readUTF();//反序列化password字段

    }

    执行结果:

?


1

2

3

4

5

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-12-03 21:08:51

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

java中transient关键字用法分析_java

本文实例分析了java中transient关键字用法.分享给大家供大家参考.具体分析如下: java有个特点就是序列化,简单地来说就是可以将这个类存储在物理空间(当然还是以文件的形式存在),那么当你从本地还原这个文件时,你可以将它转换为它本身.这可以极大地方便网络上的一些操作,但同时,因为涉及到安全问题,所以并不希望把类里面所有的东西都能存储(因为那样,别人可以通过序列化知道类里面的内容),那么我们就可以用上transient这个关键字,它的意思是临时的,即不会随类一起序列化到本地,所以当还原后

Java transient关键字使用小记_java

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

Java transient 关键字详解及实例代码_java

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

java io系列06之 序列化总结(Serializable 和 Externalizable)

本章,我们对序列化进行深入的学习和探讨.学习内容,包括序列化的作用.用途.用法,以及对实现序列化的2种方式Serializable和Externalizable的深入研究. 转载请注明出处:http://www.cnblogs.com/skywang12345/p/io_06.html 1. 序列化是的作用和用途 序列化,就是为了保存对象的状态:而与之对应的反序列化,则可以把保存的对象状态再读出来. 简言之:序列化/反序列化,是Java提供一种专门用于的保存/恢复对象状态的机制. 一般在以下几种

Java序列化与反序列化

1.什么是序列化?为什么要序列化? Java 序列化就是指将对象转换为字节序列的过程,而反序列化则是只将字节序列转换成目标对象的过程. 我们都知道,在进行浏览器访问的时候,我们看到的文本.图片.音频.视频等都是通过二进制序列进行传输的,那么如果我们需要将Java对象进行传输的时候,是不是也应该先将对象进行序列化?答案是肯定的,我们需要先将Java对象进行序列化,然后通过网络,IO进行传输,当到达目的地之后,再进行反序列化获取到我们想要的对象,最后完成通信. 2.如何实现序列化 2.1.使用到JD

Java 序列化的高级认识

引言 将 Java 对象序列化为二进制文件的 Java 序列化技术是 Java 系列技术中一个较为重要的技术点,在大部分情况下,开发人员只需要了解被序列化的类需要实现 Serializable 接口,使用 ObjectInputStream 和 ObjectOutputStream 进行对象的读写.然而在有些情况下,光知道这些还远远不够,文章列举了笔者遇到的一些真实情境,它们与 Java 序列化相关,通过分析情境出现的原因,使读者轻松牢记 Java 序列化中的一些高级认识. ----------

transient关键字使用笔记

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

java序列化2[实现Externalizable接口,可控序列化]

 * java可控制对象序列化:选择对象属性序列化,可序列化时加入当前类外其他属性  * 1.实现Externalizable接口(此接口实现了Serializable接口约定)  * 2.序列化和反序列化  * 3.控制序列化就是有选择的序列化对象(属性)  * 4.在序列化过程中储存(序列化),其他非this对象包含的数据或this信息  * 5.实现java对象和字节序列的转换  * 6.将对象的字节序列(内存)持久化到磁盘(通常为文件),高并发session处理(减轻内存压力)  * 7

Java transient关键字使用小结

1.transient关键字只能修饰变量,而不能修饰方法和类.注意,本地变量是不能被transient关键字修饰的.2.被transient关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化.3.一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问.也可以认为在将持久化的对象反序列化后,被transient修饰的变量将按照普通类成员变量一样被初始化. 如下面的例子 package com.kkoolerter