`
qindongliang1922
  • 浏览: 2186021 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
7265517b-f87e-3137-b62c-5c6e30e26109
证道Lucene4
浏览量:117583
097be4a0-491e-39c0-89ff-3456fadf8262
证道Hadoop
浏览量:125966
41c37529-f6d8-32e4-8563-3b42b2712a50
证道shell编程
浏览量:59952
43832365-bc15-3f5d-b3cd-c9161722a70c
ELK修真
浏览量:71333
社区版块
存档分类
最新评论

理解Java里面的序列化和反序列化

    博客分类:
  • JAVA
阅读更多

(一)什么是序列化和反序列化

序列化(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修饰的字段,在序列化和反射面前都是无作用的,所以一些敏感信息的序列化尤其是在网络上传输的如密码,金钱什么的,都应该考虑加密或者其他安全措施。
1
0
分享到:
评论

相关推荐

    java反序列化工具

    Java反序列化是一种将已序列化的对象状态转换回对象的过程,它是Java平台中持久化数据的一种常见方式。在Java应用程序中,序列化用于保存对象的状态以便稍后恢复,或者在网络间传输对象。然而,这个过程也可能引入...

    java 对象的序列化与反序列化

    Java对象的序列化和反序列化是Java编程中一项重要的技术,主要用于将对象的状态转换为字节流,以便存储或在网络上传输。这一过程对于理解Java的IO操作、持久化数据以及实现分布式通信等场景非常关键。 首先,我们来...

    C#和Java的序列化反序列化

    而在Java中,我们可以通过实现`Serializable`接口来使类支持序列化,或者使用`java.io.ObjectOutputStream`和`java.io.ObjectInputStream`进行对象的序列化和反序列化。 接下来,我们讨论反序列化。反序列化是序列...

    java serializable 序列化与反序列化

    **一、Java序列化** 1. **什么是序列化**:序列化是将对象的状态(属性和成员变量)转换为可以存储或传输的数据格式的过程。在Java中,通常是将对象转换为字节数组,以便写入磁盘或通过网络发送。 2. **为什么需要...

    Java Json序列化与反序列化

    Java中的JSON(JavaScript Object Notation)序列化与反序列化是开发过程中常见且重要的操作,主要用于数据交换和存储。JSON是一种轻量级的数据交换格式,它允许我们将Java对象转换为JSON字符串,反之亦然,方便在...

    java反序列化利用程序UI版Beta1.1.rar

    1. **Java序列化机制**:Java对象序列化是通过实现`Serializable`接口来标记一个类可被序列化。`ObjectOutputStream`用于将对象写入流,`ObjectInputStream`用于从流中读取并反序列化对象。 2. **易受攻击的库**:...

    java序列化(Serializable)的作用和反序列化.doc

    ### Java序列化(Serializable)的作用与反序列化详解 #### 一、序列化的概念 序列化是指将程序中的对象转换为一系列字节流的过程,主要用于保存对象的状态或在网络之间传输对象。序列化的主要目的是为了能够持久化...

    java程序,序列化和反序列化操作对文件的运用

    在Java编程语言中,序列化和反序列化是两种重要的技术,它们允许我们将对象的状态转换为字节流,以便存储到磁盘上或通过网络进行传输。这些技术在处理持久化数据、对象缓存以及跨进程通信(如RMI - 远程方法调用)时...

    Java反序列化工具.zip

    标题"Java反序列化工具.zip"暗示了这个压缩包包含了一些工具,这些工具可能是为了帮助开发者理解、测试或者防御Java反序列化漏洞而设计的。可能包括模拟攻击的payload生成器,或者用于检测和修复此类漏洞的分析工具...

    序列化和反序列化dll文件和proto

    总的来说,理解和掌握序列化和反序列化技术对于任何IT专业人员来说都是至关重要的,无论是在处理DLL文件以实现模块化开发,还是在使用Proto进行高效的数据交换。正确使用这些技术能够提高代码的可维护性、减少网络...

    hessian学习基础篇——序列化和反序列化

    在IT领域,序列化和反序列化是两个关键的概念,特别是在网络通信、数据持久化以及对象存储中。本文将深入探讨Hessian框架的基础知识,它是一个高效的二进制序列化协议,广泛应用于Java和.NET之间跨语言通信。通过...

    Java对象的序列化和反序列化实践

    在Java编程语言中,对象的序列化和反序列化是两个关键的概念,它们允许我们将对象的状态转换为可存储或传输的格式,然后再恢复为原始对象。这个过程对于数据持久化、网络传输以及跨进程通信等场景非常有用。下面将...

    学生管理系统(序列化和反序列化)

    管理系统源码.zip、README.txt 在本项目中,“学生管理系统(序列化和反序列化)”是一个基于Java或类似编程语言实现的系统,其核心功能是...在开发类似的系统时,理解并掌握序列化和反序列化的核心原理是非常重要的。

    JAVA对象的序列化与反序列化详细PPT课件.pptx

    Java对象的序列化和反序列化是Java编程中的一项重要技术,主要应用于数据持久化、网络传输等场景。本课件详细介绍了这一概念及其在实际应用中的操作。 首先,序列化是将Java对象转化为字节序列的过程,目的是为了...

    Java中的序列化与反序列化.pdf

    Java序列化是Java平台提供的一种持久化机制,它允许我们将Java对象转换成字节流,以便于存储或者在网络中传输。这一过程被称为序列化,而将字节流还原成原来的对象则称为反序列化。在Java中,实现序列化主要通过实现...

    java反序列化利用工具

    `Newtonsoft.Json.dll`是.NET框架下的Json.NET库,它提供了JSON序列化和反序列化的功能,但在这个场景下可能与Java反序列化无关,除非系统中混合使用了.NET和Java技术。`Run.exe`是一个Windows可执行文件,可能用于...

    Java中的序列化与反序列化:深入理解与实践指南

    在Java编程中,对象的序列化与反序列化是实现数据持久化和网络传输的关键技术。序列化是将对象的状态转换为字节流的过程,以便可以将其保存到文件、...希望本文能帮助你更好地理解和应用Java中的序列化与反序列化技术。

    Java对象序列化标准最新版

    ### Java对象序列化标准知识点详解 #### 一、系统架构概览 **1.1 概览** Java 对象序列化是一种将Java对象的...以上内容涵盖了Java序列化标准的关键知识点,深入了解这些概念有助于更好地理解和应用Java序列化技术。

    java中的序列化与反序列化

    Java序列化机制的优点在于它提供了一种标准的方式来处理对象的持久化和在网络间的传输。然而,序列化也存在安全风险,比如序列化可能导致远程代码执行攻击。因此,对于敏感信息或复杂对象结构,应谨慎使用序列化,并...

    Java序列化_Java序列化结构_

    3. **数据格式**:Java序列化生成的字节流是平台和版本相关的,不适用于跨平台或跨语言通信。 4. **替代方案**:Java序列化并不是唯一的选择。例如,JSON、XML、protobuf等轻量级序列化库提供了更高效、更安全的...

Global site tag (gtag.js) - Google Analytics