- 浏览: 2146742 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (682)
- 软件思想 (7)
- Lucene(修真篇) (17)
- Lucene(仙界篇) (20)
- Lucene(神界篇) (11)
- Solr (48)
- Hadoop (77)
- Spark (38)
- Hbase (26)
- Hive (19)
- Pig (25)
- ELK (64)
- Zookeeper (12)
- JAVA (119)
- Linux (59)
- 多线程 (8)
- Nutch (5)
- JAVA EE (21)
- Oracle (7)
- Python (32)
- Xml (5)
- Gson (1)
- Cygwin (1)
- JavaScript (4)
- MySQL (9)
- Lucene/Solr(转) (5)
- 缓存 (2)
- Github/Git (1)
- 开源爬虫 (1)
- Hadoop运维 (7)
- shell命令 (9)
- 生活感悟 (42)
- shell编程 (23)
- Scala (11)
- MongoDB (3)
- docker (2)
- Nodejs (3)
- Neo4j (5)
- storm (3)
- opencv (1)
最新评论
-
qindongliang1922:
粟谷_sugu 写道不太理解“分词字段存储docvalue是没 ...
浅谈Lucene中的DocValues -
粟谷_sugu:
不太理解“分词字段存储docvalue是没有意义的”,这句话, ...
浅谈Lucene中的DocValues -
yin_bp:
高性能elasticsearch ORM开发库使用文档http ...
为什么说Elasticsearch搜索是近实时的? -
hackWang:
请问博主,有用solr做电商的搜索项目?
Solr中Group和Facet的用法 -
章司nana:
遇到的问题同楼上 为什么会返回null
Lucene4.3开发之第八步之渡劫初期(八)
(一)什么是序列化和反序列化
序列化(serialization)和反序列化(deserialization)是将对象转化成字节数组以方便保存或者用于网络传输,这个对象可以是一个图片,一个字符串,一个class等等,常见序列化格式有字节数组,json格式,xml格式,更加高效的有google开源的Protocol Buffers,以及Apache Avro。
(二)为什么需要序列化和反序列化
(1)实现数据持久化,一般jvm的里面数据,在java程序退出时,所有的状态都不会保留,通过序列化可以将需要的数据给持久化到磁盘文件或者数据库,这样就可以在下次jvm启动的时候再把数据重新还原出来。
(2)利用序列化实现远程通信,即在网络上传送对象的字节序列,这种场景一般在socket或者rpc的服务中比较常见。
(三)Java里面如何实现序列化和反序列化
在java里面有两种方式可以实现对象的序列化:
(1)实现Serializable接口的类,jdk会自动帮我们序列化该类所有的信息,
但如果用户定义了writeObject和readObject方法,那么在序列化和反序列化的时候会通过反射优先调用自定义的方法
(2)实现Externalizable接口的类,需要用户自定义序列化和反序列化的逻辑,分别重写writeExternal和readExternal方法。
下面看一个例子,首先我们定义一个Person类并实现了:
````java public class Person implements Serializable { private static final long serialVersionUID = 1L;//序列化版本 private static String code="001";//code码 private transient String address;//地址 private String name;//姓名 private int age;//年龄 //getter setter 省略 //构造方法省略 //toString 方法省略 } ````
然后我们定义了帮助实现序列化和反序列化的工具类:
```` public class SerializeTools { /*** * 将任何实现了序列化接口的对象转成字节数组 * @param obj * @return * @throws Exception */ public static byte[] toBytes(Serializable obj) throws Exception{ ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream=new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(obj); return byteArrayOutputStream.toByteArray(); } /**** * 将任何序列化的字节数组,给还原成对象 * @param bytes * @return * @throws Exception */ public static Object toObj(byte[] bytes) throws Exception{ ByteArrayInputStream byteArrayInputStream=new ByteArrayInputStream(bytes); ObjectInputStream objectInputStream=new ObjectInputStream(byteArrayInputStream); Object obj=objectInputStream.readObject(); return obj; } /*** * 将一个实现了序列化的对象,给序列化成文件 * @param obj * @param storePath * @throws Exception */ public static void toFile(Serializable obj,String storePath)throws Exception{ FileOutputStream fileOutputStream=new FileOutputStream(new File(storePath)); ObjectOutputStream objectOutputStream=new ObjectOutputStream(fileOutputStream); objectOutputStream.writeObject(obj); } /*** * 将序列化文件还原成对象 * @param storePath * @return * @throws Exception */ public static Object fromFile(String storePath)throws Exception{ FileInputStream fileInputStream=new FileInputStream(new File(storePath)); ObjectInputStream objectInputStream=new ObjectInputStream(fileInputStream); Object obj=objectInputStream.readObject(); return obj; } } ````
然后看下我们的测试类,分别测试文件的序列化和字节的序列化
```` public static void main(String[] args) throws Exception { Person p1=new Person("北京海淀","张三",25); fileTest(p1);//文件测试序列化和反序列化 System.out.println("=========================="); byteTest(p1);//字节测试序列化和反序列化 } public static void fileTest(Person p1) throws Exception{ String storePath="E://temp.out"; System.out.println("基于文件序列化前:"+p1); SerializeTools.toFile(p1,storePath); Person p2=(Person) SerializeTools.fromFile(storePath); System.out.println("基于文件反序列化后:"+p2); } public static void byteTest(Person p1) throws Exception{ System.out.println("基于字节序列化前:"+p1); byte[] bytes= SerializeTools.toBytes(p1);//序列化成字节数组 Person p2=(Person) SerializeTools.toObj(bytes);//反序列化成对象 System.out.println("基于字节反序列化后:"+p2); } ````
运行后输出如下:
```` 基于文件序列化前:Person{name='张三', age=25, code=001, address=北京海淀} 基于文件反序列化后:Person{name='张三', age=25, code=001, address=null} ========================== 基于字节序列化前:Person{name='张三', age=25, code=001, address=北京海淀} 基于字节反序列化后:Person{name='张三', age=25, code=001, address=null} ````
细心的同学可能已经发现地址这个字段,在反序列化后字段值丢失了,这里说明下:
(1)在java里面transient关键词修饰的成员变量是不会被序列化的,这一点在上面的输出中已经得到验证了,注意transient关键词只能修饰成员变量,不能修饰类和方法
(2)在java里面static关键词修饰的字段也是不会被序列化的,因为这个是类的字段,而序列化是针对对象的。
引申一下:java的内存分配有栈和堆以及永久代,栈中存放基本变量,数组和对象引用,堆中存放对象,当有static修饰的变量或方法会被放到永久代里面。它先于对象而存在,不依赖实例,无论是变量,方法,还是代码块,只要用static修饰,就是在类被加载时就已经准备好了,也就是可以被使用或者已经被执行,都可以脱离对象而执行,所以在类加载时静态变量的值其实已经还原出来了之后才是反序列化出来成员变量的值。
(3)在上面的Person类里面,相信大家还看到了一个用static final long修饰的 serialVersionUID字段,这个字段的功能是用来标识类版本的兼容性:
举个例子,假如现在没有定义serialVersionUID这个字段,jdk默认是根据类信息计算一个版本值,在类已经被序列化成文件后,我们又修改了类结构,比如新增了几个字段,这个时候拿着新版本的类去反序列化旧版本的类,就会抛出下面的异常:
```` ocal class incompatible: stream classdesc serialVersionUID = 3980523453097177768, local class serialVersionUID = -2111153241298098896 at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616) at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1630) at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1521) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1781) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:373) at com.xuele.tools.tool.SerializeTools.fromFile(SerializeTools.java:57) ````意思就是版本不一致,导致失败,如果我们定义这个值,并且新旧版本的值一样,不管新增没新增字段,都可以反序列化成功,默认新增字段的值是jdk给成员变量初始化的值,比如字符串就是null。
(四)定制自己的序列化和反序列化方法
上面提到过实现了Serializable接口的类,我们可以重写下面的方法来自定义序列化逻辑:
```` private void writeObject(ObjectOutputStream out) throws Exception { System.out.println("call write"); // out.defaultWriteObject(); out.writeObject(name); out.writeInt(age); out.writeObject(address); } private void readObject(ObjectInputStream in) throws Exception { System.out.println("call read"); // in.defaultReadObject(); name = (String) in.readObject(); age=in.readInt(); address = (String) in.readObject(); } ````
再次执行测试方法:
```` 基于文件序列化前:Person{name='张三', age=25, code=001, address=北京海淀} call write call read 基于文件反序列化后:Person{name='张三', age=25, code=001, address=北京海淀} ========================== 基于字节序列化前:Person{name='张三', age=25, code=001, address=北京海淀} call write call read 基于字节反序列化后:Person{name='张三', age=25, code=001, address=北京海淀} ````
这次我们发现了被transient修饰的address字段竟然也有值了,为什么?因为我们自定义序列化的时候把地址也给序列化了,所以这个时候无论你用不用transient关键词都无关紧要了。
注意如果实现了上面的方法其实和使用Externalizable就相差无几了,所以在这里不再给出Externalizable的例子
(五)什么时候应该readObject和writeObject
在effective java里面提到过:
当一个对象的物理表示方法与它的逻辑数据内容有实质性差别时,使用默认序列化形式有N种缺陷。
其实是建议我们重写的,这样可以更好的控制序列化的过程,如果能减少一些不必要的序列化的字段,其实对我们的程序性能也是一种提升。
总结:
本文介绍了Java里面序列化和反序列化功能和使用以及一些注意事项,序列化其实还提供了深度克隆的功能,尤其是当类里面的引用层次比较多及引用特别复杂的时候,通过序列化来深度拷贝对象也是一种比较便利的方法,除此之外,我们还应该知道序列化和反序列化和反射一样,弱化了java安全权限修饰符的作用,无论你privte还是protected修饰的字段,在序列化和反射面前都是无作用的,所以一些敏感信息的序列化尤其是在网络上传输的如密码,金钱什么的,都应该考虑加密或者其他安全措施。
发表评论
-
记一次log4j不打印日志的踩坑记
2019-09-22 01:58 1458### 起因 前几天一个跑有java应用的生产集群(200多 ... -
在Java里面如何解决进退两难的jar包冲突问题?
2019-07-23 19:10 1141如上图所示: es api组件依赖guava18.0 ... -
如何轻松理解二叉树的深度遍历策略
2019-07-03 23:33 1016我们知道普通的线性数据结构如链表,数组等,遍历方式单一 ... -
为什么单线程Redis性能也很出色
2019-01-21 18:02 2132高性能的服务器,不一 ... -
如何将编程语言里面的字符串转成数字?
2019-01-11 23:23 1995将字符串转成数字在很 ... -
为什么Java里面String类是不可变的
2019-01-06 18:36 1587在Java里面String类型是不可变对象,这一点毫无疑问,那 ... -
关于Java里面volatile关键字的重排序
2019-01-04 18:49 984Java里面volatile关键字主 ... -
多个线程如何轮流打印ABC特定的次数?
2018-12-11 20:42 5930之前的一篇文章,我给 ... -
聊聊Java里面的引用传递
2018-11-16 21:21 936长久以来,在Java语言里面一直有一个争论,就是Java语言到 ... -
理解计数排序算法的原理和实现
2018-10-11 10:03 2047计数排序(Counting sort) ... -
理解Java7和8里面HashMap+ConcurrentHashMap的扩容策略
2018-09-06 11:31 3340### 前言 理解HashMap和Con ... -
关于Java里面多线程同步的一些知识
2018-07-18 09:45 1064# 关于Java里面多线程同步的一些知识 对于任何Java开 ... -
Java单例模式之双检锁深入思考
2018-07-08 12:25 3243# Java单例模式之双检锁 ... -
关于Java里面多线程同步的一些知识
2018-07-08 12:23 1084# 关于Java里面多线程同步的一些知识 对于任何Java开 ... -
重新认识同步与异步,阻塞和非阻塞的概念
2018-07-06 14:30 1426# 重新认识同步与异步 ... -
线程的基本知识总结
2018-06-27 16:27 1019### (一)创建线程的方式 (1)实现Runnable接口 ... -
Java里面volatile关键字修饰引用变量的陷阱
2018-06-25 11:42 1328# Java里面volatile关键字修饰引用变量的陷阱 如 ... -
关于Java里面的字符串拼接,你了解多少?
2018-06-25 11:28 1316# 关于Java里面的字符串 ... -
深入理解Java内存模型的语义
2018-06-25 11:39 690### 前言 Java内存模型( ... -
如何证明Java多线程中的成员变量数据是互不可见的
2018-06-21 10:09 1455前面的几篇文章主要介绍了Java的内存模型,进程和线程的定义, ...
相关推荐
Serializable接口和Externalizable接口实现序列化和反序列化
深入理解Java虚拟机-Java内存区域透彻分析(序列化、反序列化概念及其使用场景+实现序列化的方式+transient关键字)
本篇文章是对Java中对象的序列化与反序列化进行了详细的分析介绍,需要的朋友参考下
但首先要理解的是,“反序列化漏洞”是对一类漏洞的泛指,而不是专指某种反序列化方法导致的漏洞,比如Jackson反序列化漏洞和Java readObject造成的
主要为大家详细介绍了Java的序列化与反序列化,序列化是一种对象持久化的手段。普遍应用在网络传输、RMI等场景中。本文通过分析ArrayList的序列化来介绍Java序列化的相关内容,感兴趣的小伙伴们可以参考一下
Java_Serializable(序列化) 的理解和总结
本文精选了20道复杂的Java序列化面试题,并提供了详细的解析,旨在帮助你更好地理解Java序列化的原理、应用和相关问题。通过学习这些题目和解析,你将能够在面试中更自信地回答与Java序列化相关的问题,展现出你的...
帮助理解java反序列化漏洞的工具,一般还需配套工具:SerializationDumper-v1.13、DeserLab以及抓包分析工具
主要介绍了Java序列化和反序列化的相关资料,帮助大家更好的理解和学习Java,感兴趣的朋友可以了解下
个人理解,序列化和反序列化就是一种压缩和解压的过程。压缩和解压讲究一个速度和大小 2、translate 禁止序列化 3、破坏translate java 和file 、javaXML 都可以通过重写私有readObject和writeObject进行破坏...
Java反序列化漏洞学习实践中的代码 Java反序列化漏洞学习实践一:从Serializbale接口开始,先弹个计算器 Java反序列化漏洞学习实践二:Java的反射机制(Java ...Java反序列化漏洞学习实践三:理解java的动态代理机制
刚开始对java可序列化完全一片空白,看了这个文档有所理解了
Java 提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。下面小编和大家来一起学习一下吧
技术专栏 _ 深入理解JNDI注入与Java反序列化漏洞利用 企业安全 工控安全 安全对抗 安全建设 企业安全
主要介绍了Java序列化常见的三个问题,帮助大家更好的理解和学习JAVA,感兴趣的朋友可以了解下
序列化和反序列化 继承、封装、多态的实现原理 容器 Java集合类总结 Java集合详解1:一文读懂ArrayList,Vector与Stack使用方法和实现原理 Java集合详解2:Queue和LinkedList Java集合详解3:Iterator,fail-fast机制...
内容概要:以上列出的Java面试题涵盖了Java语言的基础知识、面向对象编程、集合、IO流、多线程、反射、类加载器、JVM、序列化、泛型、异常处理、注解等多个方面。 适用人群:以上Java面试题适用于准备Java开发...
序列化和反序列化 继承封装多态的实现原理 集合类 Java集合类总结 Java集合详解:一文读懂ArrayList,Vector与Stack使用方法和实现原理 Java集合详解:Queue和LinkedList Java集合详解:迭代器,快速失败机制与比较器...
主要介绍了java 中序列化与readResolve()方法的实例详解的相关资料,这里提供实例帮助大家理解这部分知识,需要的朋友可以参考下
此外,我们还探讨了对象的哈希码、重写equals()和hashCode()方法的技巧,以及对象的序列化和反序列化。 通过研究和解答这些高难度问题,您将提升自己的编程水平,展现出对Java实例概念和相关技术的深入理解。无论您...