序列化,反序列化和transient关键字

时间:2022-05-02 00:02:55

一、序列化和反序列化的概念

序列化:指把java对象转换为字节序列的过程。

反序列化:指把字节序列恢复为java对象的过程。

对象的序列化主要有两种用途:
  1) 把对象的字节序列保存到硬盘上,通常存放在一个文件中;
  2) 在网络上传送对象的字节序列。

1.当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。

2.在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。

二、JDK类库中的序列化API

java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
  java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。

只有实现了Serializable接口或Externalizable接口的类的对象才能被序列化。
  对象序列化包括如下步骤:
  1) 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流,字节数组输出流;
  2) 通过对象输出流的writeObject()方法写对象。

  对象反序列化的步骤如下:
  1) 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流,字节数组输入流;
  2) 通过对象输入流的readObject()方法读取对象。

1、类未实现Serializable接口,进行序列化的范例:

定义一个未实现Serializable的类:User

 package com.paic.egis.smts.activity;

 public class User{
private String userId;
private String userName;
public String getUserId() {
return userId;
}
@Override
public String toString() {
return "User [userId=" + userId + ", userName=" + userName + "]";
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
} }

序列化:

 package com.paic.egis.smts.activity;

 import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; public class Test { public static void main(String[] args) throws Exception{
serialize();
User o = (User) deSerialize();
System.out.println(o.toString()); } public static void serialize() throws IOException{
User u = new User();
u.setUserId("11");
u.setUserName("df");
FileOutputStream fo = new FileOutputStream(new File("d://a.txt"));
ObjectOutputStream os = new ObjectOutputStream(fo);
os.writeObject(u);
} public static Object deSerialize() throws IOException, ClassNotFoundException {
FileInputStream fi = new FileInputStream(new File("d://a.txt"));
ObjectInputStream oi = new ObjectInputStream(fi);
return oi.readObject();
} }

运行结果如下:

序列化,反序列化和transient关键字

运行报错!

2、类实现Serializable接口,进行序列化和反序列化的范例:

定义一个实现Serializable的类:UserSerialize

 package com.paic.egis.smts.activity;

 import java.io.Serializable;

 public class UserSerialize implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private String userId;
private String userName;
public String getUserId() {
return userId;
}
@Override
public String toString() {
return "User [userId=" + userId + ", userName=" + userName + "]";
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
} }

序列化和反序列化:

 package com.paic.egis.smts.activity;

 import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; public class Test { public static void main(String[] args) throws Exception{
serialize();
UserSerialize o = (UserSerialize) deSerialize();
System.out.println(o.toString()); } public static void serialize() throws IOException{
UserSerialize u = new UserSerialize();
u.setUserId("11");
u.setUserName("df");
FileOutputStream fo = new FileOutputStream(new File("d://a.txt"));
ObjectOutputStream os = new ObjectOutputStream(fo);
os.writeObject(u);
} public static Object deSerialize() throws IOException, ClassNotFoundException {
FileInputStream fi = new FileInputStream(new File("d://a.txt"));
ObjectInputStream oi = new ObjectInputStream(fi);
return oi.readObject();
} }

运行结果如下:

序列化,反序列化和transient关键字

三、serialVersionUID的作用

serialVersionUID作用:序列化时为了保持版本的兼容性,即在版本升级时反序列化仍保持对象的唯一性。

有两种生成方式:

一个是默认的1L,比如:private static final long serialVersionUID = 1L;

一个是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段,比如:private static final long  serialVersionUID = xxxxL;

下面举例说明下:

还是上面说到的类:UserSerialize,把定义的serialVersionUID去掉。

 package com.paic.egis.smts.activity;

 import java.io.Serializable;

 public class UserSerialize implements Serializable{
// /**
// *
// */
// private static final long serialVersionUID = 1L;
private String userId;
private String userName;
public String getUserId() {
return userId;
}
@Override
public String toString() {
return "User [userId=" + userId + ", userName=" + userName + "]";
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
} }

序列化和反序列化:

 package com.paic.egis.smts.activity;

 import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; public class Test { public static void main(String[] args) throws Exception{
serialize();
UserSerialize o = (UserSerialize) deSerialize();
System.out.println(o.toString()); } public static void serialize() throws IOException{
UserSerialize u = new UserSerialize();
u.setUserId("11");
u.setUserName("df");
FileOutputStream fo = new FileOutputStream(new File("d://a.txt"));
ObjectOutputStream os = new ObjectOutputStream(fo);
os.writeObject(u);
} public static Object deSerialize() throws IOException, ClassNotFoundException {
FileInputStream fi = new FileInputStream(new File("d://a.txt"));
ObjectInputStream oi = new ObjectInputStream(fi);
return oi.readObject();
} }

运行结果:

序列化,反序列化和transient关键字

是成功的。

下面我们修改下UserSerialize类:添加一个熟悉sex

 package com.paic.egis.smts.activity;

 import java.io.Serializable;

 public class UserSerialize implements Serializable{
// /**
// *
// */
// private static final long serialVersionUID = 1L;
private String userId;
private String userName; private String sex; public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getUserId() {
return userId;
}
@Override
public String toString() {
return "User [userId=" + userId + ", userName=" + userName + "]";
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
} }

这时执行反序列化操作:

 package com.paic.egis.smts.activity;

 import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; public class Test { public static void main(String[] args) throws Exception{
// serialize();
UserSerialize o = (UserSerialize) deSerialize();
System.out.println(o.toString()); } public static void serialize() throws IOException{
UserSerialize u = new UserSerialize();
u.setUserId("11");
u.setUserName("df");
FileOutputStream fo = new FileOutputStream(new File("d://a.txt"));
ObjectOutputStream os = new ObjectOutputStream(fo);
os.writeObject(u);
} public static Object deSerialize() throws IOException, ClassNotFoundException {
FileInputStream fi = new FileInputStream(new File("d://a.txt"));
ObjectInputStream oi = new ObjectInputStream(fi);
return oi.readObject();
} }

运行结果:

Exception in thread "main" java.io.InvalidClassException: com.paic.egis.smts.activity.UserSerialize; local class incompatible: stream classdesc serialVersionUID = -3074015237131537750, local class serialVersionUID = -126714174808369076
at java.io.ObjectStreamClass.initNonProxy(Unknown Source)
at java.io.ObjectInputStream.readNonProxyDesc(Unknown Source)
at java.io.ObjectInputStream.readClassDesc(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at com.paic.egis.smts.activity.Test.deSerialize(Test.java:31)
at com.paic.egis.smts.activity.Test.main(Test.java:14)

意思就是说,文件流中的class和classpath中的class,也就是修改过后的class,不兼容了,处于安全机制考虑,程序抛出了错误,并且拒绝载入。那么如果我们真的有需求要在序列化后添加一个字段或者方法呢?应该怎么办?那就是自己去指定serialVersionUID

      因此强烈建议在一个可序列化类中显示的定义serialVersionUID,为它赋予明确的值。

四、transient关键字的作用

在实际开发过程中,我们常常会遇到这样的问题,这个类的有些属性需要序列化,而其他属性不需要被序列化,打个比方,如果一个用户有一些敏感信息(如密码,银行卡号等),为了安全起见,不希望在网络操作(主要涉及到序列化操作,本地序列化缓存也适用)中被传输,这些信息对应的变量就可以加上transient关键字。换句话说,这个字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化。

java 的transient关键字为我们提供了便利,你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。

范例如下:

UserSerialize类:

package com.paic.egis.smts.activity;

import java.io.Serializable;

public class UserSerialize implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private String userId;
private String userName; private transient String sex; public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@Override
public String toString() {
return "UserSerialize [userId=" + userId + ", userName=" + userName
+ ", sex=" + sex + "]";
} }

执行序列化和反序列化

 package com.paic.egis.smts.activity;

 import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; public class Test { public static void main(String[] args) throws Exception{
serialize();
UserSerialize o = (UserSerialize) deSerialize();
System.out.println(o.toString()); } public static void serialize() throws IOException{
UserSerialize u = new UserSerialize();
u.setUserId("111");
u.setUserName("df");
u.setSex("y");
FileOutputStream fo = new FileOutputStream(new File("d://a.txt"));
ObjectOutputStream os = new ObjectOutputStream(fo);
os.writeObject(u);
} public static Object deSerialize() throws IOException, ClassNotFoundException {
FileInputStream fi = new FileInputStream(new File("d://a.txt"));
ObjectInputStream oi = new ObjectInputStream(fi);
return oi.readObject();
} }

结果如下:

序列化,反序列化和transient关键字

set字段为null,说明反序列化时根本没有从文件中获取到信息

另静态变量不管是否被transient修饰,均不能被序列化

范例:

UserSerialize类:

 package com.paic.egis.smts.activity;

 import java.io.Serializable;

 public class UserSerialize implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private String userId;
private String userName; private static String sex; public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@Override
public String toString() {
return "UserSerialize [userId=" + userId + ", userName=" + userName
+ ", sex=" + sex + "]";
} }

反序列化:

 package com.paic.egis.smts.activity;

 import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; import com.paic.pafa.exceptions.BusinessException; public class Test { public static void main(String[] args) throws Exception{
// serialize();
UserSerialize o = (UserSerialize) deSerialize();
System.out.println(o.toString());
} public static void serialize() throws IOException{
UserSerialize u = new UserSerialize();
u.setUserId("111");
u.setUserName("df");
u.setSex("yy");
FileOutputStream fo = new FileOutputStream(new File("d://a.txt"));
ObjectOutputStream os = new ObjectOutputStream(fo);
os.writeObject(u);
} public static Object deSerialize() throws IOException, ClassNotFoundException {
FileInputStream fi = new FileInputStream(new File("d://a.txt"));
ObjectInputStream oi = new ObjectInputStream(fi);
return oi.readObject();
} }

执行反序列化之前已执行过serialize()序列化操作。

执行结果如下:

序列化,反序列化和transient关键字

若同时执行序列化和反序列化,代码如下:

 package com.paic.egis.smts.activity;

 import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; public class Test { public static void main(String[] args) throws Exception{
serialize();
UserSerialize o = (UserSerialize) deSerialize();
System.out.println(o.toString());
} public static void serialize() throws IOException{
UserSerialize u = new UserSerialize();
u.setUserId("111");
u.setUserName("df");
u.setSex("yy");
FileOutputStream fo = new FileOutputStream(new File("d://a.txt"));
ObjectOutputStream os = new ObjectOutputStream(fo);
os.writeObject(u);
} public static Object deSerialize() throws IOException, ClassNotFoundException {
FileInputStream fi = new FileInputStream(new File("d://a.txt"));
ObjectInputStream oi = new ObjectInputStream(fi);
return oi.readObject();
} }

执行结果如下:

序列化,反序列化和transient关键字

可能会比较奇怪为什么sex有值,这时因为这个值并不是从文件中反序列化读出来的,是从当前JVM中对应static变量的值。

五、Externalizable接口的作用

它是Serializable接口的子类,有时我们不希望序列化那么多,可以使用这个接口,这个接口的writeExternal()和readExternal()方法可以指定序列化哪些属性;

范例如下:

UserSerialize类:

 package com.paic.egis.smts.activity;

 import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput; public class UserSerialize implements Externalizable{
/**
*
*/
private static final long serialVersionUID = 1L;
private String userId;
private String userName; private String sex; public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@Override
public String toString() {
return "UserSerialize [userId=" + userId + ", userName=" + userName
+ ", sex=" + sex + "]";
}
@Override
public void writeExternal(ObjectOutput objectoutput) throws IOException {
objectoutput.writeObject(userId); }
@Override
public void readExternal(ObjectInput objectinput) throws IOException,
ClassNotFoundException {
userId = (String) objectinput.readObject(); } }

序列化和反序列化:

 package com.paic.egis.smts.activity;

 import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; public class Test { public static void main(String[] args) throws Exception{
serialize();
UserSerialize o = (UserSerialize) deSerialize();
System.out.println(o.toString());
} public static void serialize() throws IOException{
UserSerialize u = new UserSerialize();
u.setUserId("111");
u.setUserName("df");
u.setSex("yy");
FileOutputStream fo = new FileOutputStream(new File("d://a.txt"));
ObjectOutputStream os = new ObjectOutputStream(fo);
os.writeObject(u);
} public static Object deSerialize() throws IOException, ClassNotFoundException {
FileInputStream fi = new FileInputStream(new File("d://a.txt"));
ObjectInputStream oi = new ObjectInputStream(fi);
return oi.readObject();
} }

执行结果如下:

序列化,反序列化和transient关键字

序列化,反序列化和transient关键字的更多相关文章

  1. Java对象表示方式1:序列化、反序列化和transient关键字的作用

    平时我们在Java内存中的对象,是无法进行IO操作或者网络通信的,因为在进行IO操作或者网络通信的时候,人家根本不知道内存中的对象是个什么东西,因此必须将对象以某种方式表示出来,即存储对象中的状态.一 ...

  2. 序列化、反序列化和transient关键字的作用

    引言 将 Java 对象序列化为二进制文件的 Java 序列化技术是 Java 系列技术中一个较为重要的技术点,在大部分情况下,开发人员只需要了解被序列化的类需要实现 Serializable 接口, ...

  3. 简述serializable和transient关键字作用

    transient的作用及使用方法,官方解释为: Variables may be marked transient to indicate that they are not part of the ...

  4. transient关键字的理解

    谈到这个transient这个关键字,我们应该会立马想到序列化这个过程:什么是序列化?什么又是反序列化呢?序列化就是将对象转化内成二进制,而反序列化就是就二进制文件转换成对象的过程.一旦变量使用了tr ...

  5. java中的transient关键字详解

    目录 1.何谓序列化? 2.为何要序列化? 3.序列化与transient的使用 4.java类中serialVersionUID作用 5.transient关键字小结 前言 说实话学了一段时间jav ...

  6. transient关键字和serialVersionUID

    此文章很大部分转载于Java的架构师技术栈微信公众号,博主均测试通过加上自己理解写出 最近阅读java集合的源码,发现transient关键字,就了解了一下他的用法,transient关键字一般在实现 ...

  7. Java基础之instanceof和transient关键字用法

    instanceof 用于检测指定对象是否是某个类(本类.父类.子类.接口)的实例.Java中的instanceof也称为类型比较运算符,因为它将类型与实例进行比较. 返回true或false. 如果 ...

  8. K:java序列化与反序列化—transient关键字的使用

      首先,应该明白的是transient是java中的一个关键字,音标为 英: [ˈtrænziənt].   在了解transient关键字之前,应该先弄明白序列化和反序列化.所谓的序列化,通俗点的 ...

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

    1. transient的作用及使用方法 我们都知道一个对象只要实现了Serilizable接口,这个对象就可以被序列化,java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过 ...

随机推荐

  1. JS控制键盘录入 和 window.event.keycode对照

    一.只允许录入整数 1.不允许录入非数字(按下字母键就会提示并清空) function intOnly() { if (!(window.event.keyCode >= 48 &&am ...

  2. Pycharm5注册方式

    0x1 ,安装 0x2 , 调整时间到2038年. 0x3 ,申请30天试用 0x4, 退出pycharm 0x5, 时间调整回来. ##注册方法2### 注册方法:    在 注册时选择 Licen ...

  3. Daily Scrum Meeting ——FifthDay

    一.Daily Scrum Meeting照片 牛姐去工程师那边了,已经在群里给我汇报了.橙汁去北京参加ICPC了 二.Burndown Chart 渐入佳境了,最后一礼拜了 三.项目进展 1.实现注 ...

  4. Curse of Dimensionality

    Curse of Dimensionality Curse of Dimensionality refers to non-intuitive properties of data observed ...

  5. 对于Oracle中分页排序查询语句执行效率的比较分析

    转自:http://bbs.csdn.net/topics/370033478 对于Oracle中分页排序查询语句执行效率的比较分析 作者:lzgame 在工作中我们经常遇到需要在Oracle中进行分 ...

  6. asp.net推送

    http://tech.it168.com/a2012/0210/1310/000001310252_all.shtml http://www.infoq.com/cn/news/2012/09/rc ...

  7. SuperSocket源码解析之消息处理

    一 简述 Tcp消息的处理本身是与Tcp消息传输过程独立的,是消息的两个不同阶段,从前面的会话生命周期我们已经知道消息的传输主要有SocketSession实现,而真正处理则交由AppSession实 ...

  8. javaScript【创建对象、创建类、成员变量、方法、公有和私有、静态】

    创建对象 方式① 直接使用new Object() var obj = new Object(); 方式② 使用空的{}大括号 var obj2 = {}; 测试 增加属性,访问属性 我们要为创建的对 ...

  9. 八、springboot(五)配置定时器

    1.修改启动类 @SpringBootApplication @EnableScheduling public class DemoApplication { public static void m ...

  10. Linux 内核态与用户态通信 netlink

    参考资料: https://blog.csdn.net/zqixiao_09/article/details/77131283 https://www.cnblogs.com/lopnor/p/615 ...