java-序列化与反序列化

序列化和反序列化的概念

  1. 序列化:把java对象转换为字节序列的过程称为对象的序列化,这些字节序列可以被保存在磁盘上或通过网络传输,以备以后重新恢复原来的对象
  2. 反序列化:把字节序列恢复为对象的过程称为对象的反序列化。序列化机制使得对象可以脱离程序的运行而独立存在

序列化的功能/用途

  1. 持久化对象:Java平台允许我们在内存中创建可复用的Java对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在,即,
    这些对象的生命周期不会比JVM的生命周期更长。但在现实应用中,就可能要求在JVM停止运行之后能够保存(持久化)指定的对象,并在将来重新读取被保存的对象。把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中,以此实现该功能 。java中的对象的内部状态只保存在内存中,其生命周期最长与JVM的生命周期一样,即JVM停止之后,所有对象都会被销毁。
  2. 网络传输:在网络上传送对象的字节序列。

实际应用

  1. 在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。
  2. 当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。

实现

  1. java.io.Serializable接口,那么它就可以被序列化
  2. Externalizable:
    Serializable接口
    · 优点:内建支持
    · 优点:易于实现
    · 缺点:占用空间过大
    · 缺点:由于额外的开销导致速度变比较慢
    Externalizable接口
    · 优点:开销较少(程序员决定存储什么)
    · 优点:可能的速度提升
    · 缺点:虚拟机不提供任何帮助,也就是说所有的工作都落到了开发人员的肩上。
    在两者之间如何选择要根据应用程序的需求来定。Serializable通常是最简单的解决方案,但是它可能会导致出现不可接受的性能问题或空间问题;在出现这些问题的情况下,Externalizable可能是一条可行之路。

JDK类库中的序列化API

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

序列化与反序列化的编程实现

实现序列化接口的类

public class Person implements Serializable{
    private static final long serialVersionUID = -1015228989208411177L;
    private String name;   
        private  int age;  
    public Person(String name, int age) {    
        this.name = name;   
        this.age = age;   
    }   
    public String getName() {   
        return name;   
    }   
    public void setName(String name) {   
        this.name = name;   
    }      
    public int getAge() {   
        return age;   
    }      
    public void setAge(int age) {   
        this.age = age;   
    }   
}

序列化过程

public class WriteObject {
    public static void main(String[] args) {
        ObjectOutputStream oos = null;   
        try {   
            //1.创建一个ObjectOutputStream   
            oos = new ObjectOutputStream(new FileOutputStream("/home/sunyan/object.txt"));   
            Person per = new Person("孙悟空", 500);  
            //2.将per对象写入输入流   
            oos.writeObject(per); 
        } catch (FileNotFoundException e) {   
             e.printStackTrace();   
        } catch (IOException e) {   
             e.printStackTrace();   
        }finally{   
            try {   
                if(oos != null){   
                    oos.close();   
                }   
            } catch (IOException e) {   
                 e.printStackTrace();   
            }   
        }   
    }
}

反序列化

public class ReadObject {
    public static void main(String[] args) {
        ObjectInputStream ois = null;              
        try {   
            //1.创建一个ObjectInputStream输入流   
           ois = new ObjectInputStream(new FileInputStream("/home/sunyan/object.txt"));   
            //2.从输入流中读取一个Java对象,并将其强制类型转换为Person对象   
            Person p = (Person) ois.readObject();
            System.out.println("名字为:" + p1.getName() + "\n年龄为:" + p1.getAge());
        } catch (FileNotFoundException e) {   
            e.printStackTrace();   
        } catch (IOException e) {   
            e.printStackTrace();   
        } catch (ClassNotFoundException e) {   
            e.printStackTrace();   
        }finally{   
            try {   
                if (ois == null) {   
                     ois.close();   
                }   
            } catch (IOException e) {   
                e.printStackTrace();   
            }   
        }   
    }
}

执行结果:

  1. 如果我们向文件中使用序列化机制写入了多个Java对象,使用反序列化机制恢复对象必须按照实际写入的顺序读取。
    序列化
 Person per1 = new Person("孙悟空", 500);  
 Person per2 = new Person("孙小妹", 50); 
oos.writeObject(per1); 
 oos.writeObject(per2); 

反序列化

Person p1 = (Person) ois.readObject();
Person p2 = (Person) ois.readObject();   
System.out.println("名字为:" + p1.getName() + "\n年龄为:" + p1.getAge());
System.out.println("名字为:" + p2.getName() + "\n年龄为:" + p2.getAge()); 

执行结果


  1. 对象引用的序列化
    如果类的属性不是基本类型或者String类型,而是另一个引用类型,那么这个引用类型必须是可序列化的,否则该类也是不可序列化的,即使该类实现了Serializable,Externalizable接口。
public class Teacher implements Serializable{
    private String name;   
    //类的属性是引用类型,也必须序列化。   
    //如果Person是不可序列化的,无论Teacher实现Serializable,Externalizable接口,则Teacher   
    //都是不可序列化的。   
    private Person student;   
    public Teacher(String name, Person student) {   
        super();   
        this.name = name;   
        this.student = student;   
    }   
    public String getName() {   
         return name;   
    }    
    public void setName(String name) {   
         this.name = name;   
    }      
    public Person getStudent() {   
         return student;   
    }    
    public void setStudent(Person student) {   
        this.student = student;   
    }   
}

上述代码中,Teacher中有一个引用类型student,若Person未实现接口Serializable。即

public class Person implements Serializable{
}

则在序列化过程中,会报错


  1. Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。
    在Person中更改如下代码
private  transient int age; 

此时1中的代码,经序列化和反序列化后,执行结果如下


  1. s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D
    s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D​:​ ​字​面​意​思​上​是​序​列​化​的​版​本​号​,凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量。序列化 ID 是否一致,决定 虚拟机是否允许反序列化。
    实现Serializable接口的类如果类中没有添加serialVersionUID,那么就会出现如下的警告提示



    serialVersionUID有两种生成方式:
    采用 Add default serial version ID
    这种方式生成的serialVersionUID是1L,例如:

 private static final long serialVersionUID = 1L;

采用 Add generated serial version ID这种方式生成的serialVersionUID是根据类名,接口名,方法和属性等来生成的,例如:

private static final long serialVersionUID = -1015228989208411177L;

对1中的代码序列化后,修改

private static final long serialVersionUID = -1015228989208411177L;

private static final long serialVersionUID = -1015228989208411178L;

此时,进行反序列化,会报错


反序列化漏洞危害

当应用代码从用户接受序列化数据,并试图反序列化改数据进行下一步处理时,会产生反序列化漏洞,其中最有危害性的就是远程代码注入。
这种漏洞产生原因是,java类ObjectInputStream在执行反序列化时,并不会对自身的输入进行检查,这就说明恶意攻击者可能也可以构建特定的输入,在 ObjectInputStream类反序列化之后会产生非正常结果,利用这一方法就可以实现远程执行任意代码。

最后再加一些相关知识点

1、声明为static和transient的成员数据不能被串行化,因为static代表类的状态,transient代表对象的临时数据。

2、要想将父类对象也序列化,就需要让父类也实现Serializable 接口。
3、服务器端给客户端发送序列化对象数据,对象中有一些数据是敏感的,比如密码字符串等,希望对该密码字段在序列化时,进行加密,而客户端如果拥有解密的密钥,只有在客户端进行反序列化时,才可以对密码进行读取,这样可以一定程度保证序列化对象的数据安全。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,175评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,674评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,151评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,597评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,505评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,969评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,455评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,118评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,227评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,213评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,214评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,928评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,512评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,616评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,848评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,228评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,772评论 2 339

推荐阅读更多精彩内容

  • 一、 序列化和反序列化概念 Serialization(序列化)是一种将对象以一连串的字节描述的过程;反序列化de...
    步积阅读 1,433评论 0 10
  • 原帖地址:原帖个人网站地址:个人网站简书对markdown的支持太完美了,我竟然可以直接Ctrl C/V过来。 定...
    ryderchan阅读 3,773评论 1 9
  • 简介 对于一个存在于Java虚拟机中的对象来说,其内部的状态只保持在内存中。JVM停止之后,这些状态就丢失了。在很...
    FX_SKY阅读 784评论 0 0
  • 问题 Java序列化与反序列化是什么?为什么需要序列化与反序列化?有什么好处?如何实现Java序列化与反序列化? ...
    海边的卡夫卡丶阅读 382评论 1 1
  • 序列化的意义 1.永久存储某个jvm中运行时的对象。2.对象可以网络传输3.rmi调用都是以序列化的方式传输参数 ...
    炫迈哥阅读 642评论 0 0