Serializable深入理解

Posted codetree

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Serializable深入理解相关的知识,希望对你有一定的参考价值。

1.什么是序列化,解决什么问题

序列化可以对象的状态信息转换成可以持久化或者可以传输形式的过程。一般是转为字节数据。而把字节数组还原成原来同等对象的过程成为反序列化。

在Java中,对象的序列化与反序列化被广泛应用到RMI(远程方法调用)及网络传输中。

2.如何使用序列化

2.1 类通过实现java.io.Serializable接口可以实现序列化

满足序列化的条件包括:

  • 发送方和接收方都要有相同的类(类信息和类路径)
  • 要有一致的序列化id

举例说明:


/**
 * 发送端
 * @author hasee
 *
 */
public class Client {
    private static final String URL = "127.0.0.1";
    private static final int PORT = 1111;
    public static void main(String[] args) throws UnknownHostException, IOException {
        Socket socket = new Socket(URL,PORT);
        User user = new User("luke",29);
        OutputStream out = socket.getOutputStream();
        ObjectOutputStream objectOutStream = new ObjectOutputStream(out);
        objectOutStream.writeObject(user);
        System.out.println("客户端发送完毕");
        objectOutStream.close();
        socket.close();
    }
}


package hellodemo.java.serializable.vo;

import java.io.Serializable;
/**
 * 客户端侧用户的对象。和服务端的用户对象类一致。而且序列化id一致。
 * @author hasee
 *
 */
public class User implements Serializable{
    private static final long serialVersionUID = 1L;
    private String name;
    @Override
    public String toString() {
        return "User [name=" + name + ", id=" + id + "]";
    }
    private int id;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public User(String name, int id) {
        super();
        this.name = name;
        this.id = id;
    }
    
    
    
}
/**
 * 接收端
 * @author hasee
 *
 */
public class Server {
    private static final String URL = "127.0.0.1";
    private static final int PORT = 1111;
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ServerSocket serverSocket = new ServerSocket();
        serverSocket.bind(new InetSocketAddress(URL,PORT));
        //服务端一致监听来自客户端的请求
        ObjectInputStream objectInputSteam = null;
        while(true) {
            Socket socket = serverSocket.accept();
            if(socket.isConnected()) {
                System.out.println("接受来自客户端的连接请求成功。。。");
                objectInputSteam = new ObjectInputStream(socket.getInputStream());
                User user = (User) objectInputSteam.readObject();
                System.out.println("反过序列化后user:" + user);
                objectInputSteam.close();
            }
        }
        
    }

package hellodemo.java.serializable.vo;

import java.io.Serializable;
/**
 * 服务侧用户的对象。和客户端的用户对象类一致。而且序列化id一致。
 * @author hasee
 *
 */
public class User implements Serializable{
    private static final long serialVersionUID = 1L;
    private String name;
    @Override
    public String toString() {
        return "User [name=" + name + ", id=" + id + "]";
    }
    private int id;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public User(String name, int id) {
        super();
        this.name = name;
        this.id = id;
    }
    
    
    
}

    

输出结果:

请求方:

客户端发送完毕

接收方:

接受来自客户端的连接请求成功。。。
反过序列化后user:User [name=luke, id=29]

2.2 注意事项

Transient 关键字的使用

被Transient修饰的变量,在反序列化时无法正确序列化,其值会被置为初始化值。int类型变量会被置为0,引用类型变量会被置为null。

...
private transient String name;//使用关键之transient修饰你不许希望序列化的变量
...

接受来自客户端的连接请求成功。。。
反过序列化后user:User [name=null, id=29]

序列化ID

能否序列化成功要求被序列化和发序列化的2个类具有相同的序列化id。序列化id的生成策略有2个:

  • 默认是 1L
  • 使用jdk工具自动生成一个随机的id

类继承关系中的序列化

  • 如果父类实现了Serializable,子类不需要重复实现Serializable接口也可以被序列化。
  • 如果子类实现了Serializable而父类没有实现Serializable接口的情况。虽然java里面创建子类对象前会先创建父类对象。但由于父类没有实现Serializable接口,所以父类的自有变量的值不会被序列化。而是初始化值,int类型变量会被置为0,引用类型变量会被置为null。

3.第三方的序列化插件使用

3.1 为什么会有第三方的序列化

主要2个原因:

  • java序列化的对象必须要通过java语言来进行反列化。这限制了其他语言开发的平台对对象进行反序列化。
  • 在生产之中,如果你需要对一个很大的对象或者需要频繁进行序列化操作,java提供的序列化机制性能低。(偶尔一个对象的序列化,还是可以直接用jdk提供的现成方案)

3.2 常用的第三方序列化支持

protobuf

kyno

mesagepack

 官方解释:MessagePack is an efficient binary serialization format. It lets you exchange data among multiple languages like JSON. But it‘s faster and smaller. Small integers are encoded into a single byte, and typical short strings require only one extra byte in addition to the strings themselves.

官方提供的使用demo:

首先引入依赖:
  <dependency>
    <groupId>org.msgpack</groupId>
    <artifactId>msgpack</artifactId>
    <version>0.6.12</version>
  </dependency>

其次使用demo:
package cn.enjoyedu.messagepack;
/**
 * 测试MessagePack序列化功能
 * @author hasee
 *
 */

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.msgpack.MessagePack;
import org.msgpack.template.Templates;
import org.msgpack.type.Value;
import org.msgpack.unpacker.Converter;

public class TestMessagePack {
    public static void main(String[] args) throws IOException {
        // Create serialize objects.
        List<String> src = new ArrayList<String>();
        src.add("msgpack");
        src.add("kumofs");
        src.add("viver");
        
        MessagePack msgpack = new MessagePack();
        // Serialize
        byte[] raw = msgpack.write(src);
        
        // Deserialize directly using a template
        List<String> dst1 = msgpack.read(raw, Templates.tList(Templates.TString));
        System.out.println(dst1.get(0));
        System.out.println(dst1.get(1));
        System.out.println(dst1.get(2));
        
        // Or, Deserialze to Value then convert type.
        Value dynamic = msgpack.read(raw);
        List<String> dst2 = new Converter(dynamic).read(Templates.tList(Templates.tString()));
        System.out.println(dst2.get(0));
        System.out.println(dst2.get(1));
        System.out.println(dst2.get(2));
        
    }
}


outPut:
    msgpack
    kumofs
    viver
    msgpack
    kumofs
    viver

以上是关于Serializable深入理解的主要内容,如果未能解决你的问题,请参考以下文章

深入理解Java中的String

深入理解Java中的String

深入理解DOM节点类型第四篇——文档片段节点DocumentFragment

深入理解String类(重点)

深入理解PHP原理之Opcodes

获取 Intent 片段上的 Serializable ArrayList