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

序列化:指把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();
} }

运行结果如下:

运行报错!

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

运行结果如下:

三、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();
} }

运行结果:

是成功的。

下面我们修改下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();
} }

结果如下:

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()序列化操作。

执行结果如下:

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

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

执行结果如下:

可能会比较奇怪为什么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关键字的更多相关文章

  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. 关系数据库—SQL学习笔记

    SQL的特点: 综合统一 高度非过程化(存取路径的选择以及SQL的操作过程由系统自动完成) 面向集合的操作方式,以同一种语法结构提供多种使用方法(可以在终端键盘上直接键入SQL命令对数据库进行操作,也 ...

  2. git 远程仓库

    1.查看当前的远程库 git remote git remote -v 2.添加远程仓库 git remote add [shortname] [url] 3.从远程仓库抓取数据 git fetch ...

  3. 懵懂的js原型

    说起原型,就得提起函数,js中据说是这样定义的,每个函数都有一个prototype 属性,(这个属性啊,又是个对象).为了构造函数能复用,就引出了原型,要把共享的东西放到原型上去.例如 //构造函数, ...

  4. checkbox全选与非全选之间的切换

    <div id="congras_area"> <input type="checkbox" name="" id=&qu ...

  5. Python 静态方法、类方法

    今天我们来讨论一下Python类中所存在的特殊方法--静态方法.类方法. 一.定义 静态方法: 一种简单函数,符合以下要求: 1.嵌套在类中. 2.没有self参数. 特点: 1.类调用.实例调用,静 ...

  6. Nike Zoom Winflo 2 Kvinder Sko Når jeg set elementet

    De fleste af os elskede denne Nike Pegasus 34 foruden var ved at blive begejstret for at få dine ben ...

  7. 深入浅出学Spring Data JPA

    第一章:Spring Data JPA入门 Spring Data是什么 Spring Data是一个用于简化数据库访问,并支持云服务的开源框架.其主要目标是使得对数据的访问变得方便快捷,并支持map ...

  8. ubuntu右上角时间不显示

    重启unity sudo killall unity-panel-service

  9. 红米2A高配刷机记录

    2014816 机型:红米2A高配版 设备型号:2014816 CPU:高通 线刷:fastboot平台 http://192.168.7.118/MesReports/Reports/Cutting ...

  10. [Java] Maven 建立 Spring MVC 工程

    GIT: https://github.com/yangyxd/Maven.SpringMVC.Web 1. 建立 WebApp 工程 下一步: 下一步: 选择 maven-archetype-web ...