Java序列化专题

Java序列化专题

为什么要有序列化:

简单来说序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化,流的概念这里不用多说(就是I/O),我们可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间(注:要想将对象传输于网络必须进行流化)!在对对象流进行读写操作时会引发一些问题,而序列化机制正是用来解决这些问题的!

恰当的序列化协议不仅可以提高系统的通用性、强壮型、安全性、优化性能。同时还能让系统更加易于调试和扩展。

序列化实现方式:

Java本身的序列化机制

Java语言本身提供了实现对象的序列化的方式,需要通过实现Serializable接口。然后采用ObjectOutPutStream以及ObjectInputStream进行对象的序列化、反序列化。在序列化时还需要添加serialVersuibUID。

实体类
package com.fish.bone;
    	    
    	    import java.io.Serializable;
    	    
    	    /**
    	     * @program: JavaStudyTest
    	     * @Date: 2019/3/19 0019 上午 8:53
    	     * @Author: <.*)#)))<
    	     * @Description:
    	     */
    	    public  class Fish implements Serializable {
    	    
    	        private static final long serialVersionUID = 7176862405761602267L;
    	    
    	        private String liveIn;
    	    
    	        private int length;
    	    
    	        private int weight;
    	    
    	        public String getLiveIn() {
    	            return liveIn;
    	        }
    	    
    	        public void setLiveIn(String liveIn) {
    	            this.liveIn = liveIn;
    	        }
    	    
    	        public int getLength() {
    	            return length;
    	        }
    	    
    	        public void setLength(int length) {
    	            this.length = length;
    	        }
    	    
    	        public int getWeight() {
    	            return weight;
    	        }
    	    
    	        public void setWeight(int weight) {
    	            this.weight = weight;
    	        }
    	    
    	        @Override
    	        public String toString() {
    	            return "Fish{" +
    	                    "liveIn='" + liveIn + '\'' +
    	                    ", length=" + length +
    	                    ", weight=" + weight +
    	                    '}';
    	        }
    	    }
测试类
package com.fish.bone;

import java.io.*;

/**
 * Hello world!
 *
 */
public class App 
{
    public static void main( String[] args ) throws IOException, ClassNotFoundException {
        SerializeObject();
        DeSerializeObject();
    }

    private static void SerializeObject() throws IOException {
        Fish fish = new Fish();
        fish.setLength(2);
        fish.setLiveIn("大海");
        fish.setWeight(30);

        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(new File("Fish")));
        objectOutputStream.writeObject(fish);

        objectOutputStream.flush();
        System.out.println("序列化完成");
    }

    private static void DeSerializeObject() throws IOException, ClassNotFoundException {
        ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("Fish"));
        Fish fish = (Fish) inputStream.readObject();
        System.out.println(fish);
        System.out.println("反序列化完成");
    }


}

序列化文件内容

由于没有安装二进制插件。就先将就看一下。
序列化文件内容

需要注意
serialVersionUID作用:

简单来说,Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException。(例如:在序列化时加入serialVersionUID,进行反序列化时注销serialVersionUID,就会出现反序列化失败)

Exception in thread "main" java.io.InvalidClassException: com.fish.bone.Fish; local class incompatible: stream classdesc serialVersionUID = 7176862405761602267, local class serialVersionUID = 8189052949692937727
	at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699)
	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1885)
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1751)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2042)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)
	at com.fish.bone.App.DeSerializeObject(App.java:39)
	at com.fish.bone.App.main(App.java:14)

static静态变量序列化:

在类中存在静态变量,并对该类进行序列化时是不会保存静态变量状态的。简单来说类中的static静态变量是不能被序列化的,反序列化后类中static静态变量的值为当前JVM中对应static变量的值,这个值是JVM中的不是反序列化得出的。

package com.fish.bone;

import java.io.Serializable;

/**
 * @program: JavaStudyTest
 * @Date: 2019/3/19 0019 上午 8:53
 * @Author: <.*)#)))<
 * @Description:
 */
public  class Fish implements Serializable {

    private static final long serialVersionUID = 7176862405761602267L;

    private String liveIn;

    public static int length =2;

    private int weight;

    public String getLiveIn() {
        return liveIn;
    }

    public void setLiveIn(String liveIn) {
        this.liveIn = liveIn;
    }


    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }

    @Override
    public String toString() {
        return "Fish{" +
                "liveIn='" + liveIn + '\'' +
                ", length=" + length +
                ", weight=" + weight +
                '}';
    }
}

package com.fish.bone;

import java.io.*;

/**
 * Hello world!
 *
 */
public class App 
{
    public static void main( String[] args ) throws IOException, ClassNotFoundException {
        SerializeObject();
        Fish.length = 5;
        DeSerializeObject();
    }

    private static void SerializeObject() throws IOException {
        Fish fish = new Fish();
        //fish.setLength(2);
        fish.setLiveIn("大海");
        fish.setWeight(30);

        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(new File("Fish")));
        objectOutputStream.writeObject(fish);
        objectOutputStream.flush();

        System.out.println("序列化完成");

        fish.setLiveIn("河流");

        objectOutputStream.writeObject(fish);
        objectOutputStream.flush();
        objectOutputStream.close();
        System.out.println("序列化修改完成");
    }

    private static void DeSerializeObject() throws IOException, ClassNotFoundException {
        ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("Fish"));
        Fish fish = (Fish) inputStream.readObject();
        System.out.println(fish);

        //Fish fish2 = (Fish) inputStream.readObject();
        System.out.println(fish.length);
        System.out.println("反序列化完成");
    }


}
输出结果:
	序列化完成
	序列化修改完成
	Fish{liveIn='大海', length=5, weight=30}
	5
	反序列化完成

Process finished with exit code 0

如上所示,如果静态变量参与序列化那么输出结果应该是序列化时的值2,而不是序列化之后才修改为的5。
此处有一个问题:
在我对Fish序列化之后,尝试在原对象修改LiveIn字段的值重新进行序列化。在反序列化后读取到的值还是之前的。这个原因暂时还不清楚。如果重新new对象读取则没有问题。

备注:
  1. 在通过实现Serializable接口进行序列化时,可以使用Transient关键字修饰非静态字段表示该字段不参与序列化。如果没有被序列化的成员变量反序列化后,会被设置成初始值,比如String -> null,int ->0。
  2. 静态字段无论是否用Transient均不参与序列化。
  3. 如果父类没有实现序列化,而子类实现序列化。那么在序列化子类时父类中的成员没办法做序列化操作。
  4. 深拷贝可以通过序列化方式实现。(记得实现Serializable接口)
public Object deepClone() throws IOException, ClassNotFoundException {
       //序列化
       ByteArrayOutputStream baos=new ByteArrayOutputStream();
       ObjectOutputStream oos=new ObjectOutputStream(baos);

       oos.writeObject(this);

       //反序列化
       ByteArrayInputStream bais=new ByteArrayInputStream(baos.toByteArray());
       ObjectInputStream ois=new ObjectInputStream(bais);
       return ois.readObject();
   }
  1. 对同一个对象进行多次写入,打印出的第一次存储结果和第二次存储结果,只多了5个字节的引用关系。
    并不会导致文件累加。
Java本身序列化存在的问题
  1. 序列化数据结果比较大、传输效率比较低。
  2. 不能跨语言对接。
Java序列化的演变过程

由于Java本身序列化的问题,以至于在后来很长一段时间内,基于XML格式编码的对象序列化机制成为了主流,一方面解决了多语言兼容问题,另一方面比二进制的序列化方式更容易理解。以至于基于XML的SOAP协议及对应的WebService框架在很长一段时间内成为各个主流开发语言的必备的技术。

再到后来,基于JSON的简单文本格式编码的HTTP REST接口又基本上取代了复杂的Web Service接口,成为分布式架构中远程通信的首要选择。但是JSON序列化存储占用的空间大、性能低等问题,同时移动客户端应用需要更高效的传输数据来提升用户体验。在这种情况下与语言无关并且高效的二进制编码协议就成为了大家追求的热点技术之一。首先诞生的一个开源的二进制序列化框架-MessagePack。它比google的Protocol Buffers出现得还要早。

主流的序列化技术

JSON

FastJson/Gson/JackSon序列化和反序列化性能对比

Hessian(Hessian2)

XML

XML优缺点

XML的优点:
  A.格式统一,符合标准;
  B.容易与其他系统进行远程交互,数据共享比较方便。
XML的缺点:
  A.XML文件庞大,文件格式复杂,传输占带宽;
  B.服务器端和客户端都需要花费大量代码来解析XML,导致服务器端和客户端代码变得异常复杂且不易维护;
  C.客户端不同浏览器之间解析XML的方式不一致,需要重复编写很多代码;
  D.服务器端和客户端解析XML花费较多的资源和时间。

Protobuffer

Kryo

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值