我是靠谱客的博主 魁梧月饼,最近开发中收集的这篇文章主要介绍Java Serializable接口(序列化),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

Java Serializable接口(序列化)

1、是什么
是启用其序列化功能的接口。实现java.io.Serializable 接口的类是可序列化的。没有实现此接口的类将不能使它们的任意状态被序列化或逆序列化。

序列化是指把对象转换为字节序列的过程,我们称之为对象的序列化,就是把内存中的这些对象变成一连串的字节(bytes)描述的过程。

反序列化则相反,就是把持久化的字节文件数据恢复为对象的过程。那么什么情况下需要序列化呢?大概有这样两类比较常见的场景:1)、需要把内存中的对象状态数据保存到一个文件或者数据库中的时候,这个场景是比较常见的,例如我们利用mybatis框架编写持久层insert对象数据到数据库中时;2)、网络通信时需要用套接字在网络中传送对象时,如我们使用RPC协议进行网络通信时;

序列化的过程,就是一个“freeze”的过程,它将一个对象freeze(冷冻)住,然后进行存储,等到再次需要的时候,再将这个对象de-freeze就可以立即使用。

2、为什么
当我们需要把对象的状态信息通过网络进行传输,或者需要将对象的状态信息持久化,以便将来使用时都需要把对象进行序列化。

3、怎么做

public class User implements Serializable { 
    private static final long serialVersionUID = 1L; 
 
    private String userId; 
    private String userName; 
 
    public User(String userId, String userName) { 
        this.userId = userId; 
        this.userName = userName; 
    } 
} 

这个实体类实现了Serializable接口,表示可序列化。Serializable接口是一个里面什么都没有的接口,它的源代码是public interface Serializable{}。如果一个接口里面什么内容都没有,那么这个接口是一个标识接口,比如,一个学生遇到一个问题,排错排了几天也没解决,此时,她举手了(示意我去帮他解决),然后我过去,帮他解决了,那么这个举手其实就是一个标识,自己不能解决的问题标示我去帮他解决,在Java中的这个Serializable接口是给JVM看的,告诉JVM,我不做这个类的序列化了,你(JVM)给我序列化,序列化就是变成二进制流,比如云计算、Hadoop,特别是Hadoop完全就是分布式环境,那么就要涉及到对象要在网络中传输,里面的全是二进制流,当然你来做这个序列化操作也可以,但是这个类里面可能还有一个类,如果你把外面的类对象User变成二进制,那么里面也要序列化(这要用到深度遍历,很麻烦),干脆告诉JVM,让他来帮你做。

然后我们编写测试类,来对该对象进行读写操作,我们先测试将该对象写入一个文件:

public class SerializableTest { 
 
    /** 
     * 将User对象作为文本写入磁盘 
     */ 
    public static void writeObj() { 
        User user = new User("1001", "Joe"); 
        try { 
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("/Users/guanliyuan/user.txt")); 
            objectOutputStream.writeObject(user); 
            objectOutputStream.close(); 
        } catch (IOException e) { 
            e.printStackTrace(); 
        } 
    } 
 
    public static void main(String args[]) { 
        writeObj(); 
    } 
} 

生成的user.txt文件:

java.io.NotSerializableException: cn.wudimanong.serializable.User 
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184) 
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348) 
    at cn.wudimanong.serializable.SerializableTest.writeObj(SerializableTest.java:19) 
    at cn.wudimanong.serializable.SerializableTest.main(SerializableTest.java:27) 

如果User类没有实现可序列化接口,则报异常NotSerializableException。

接下来,我们继续编写测试代码,尝试将之前持久化写入user.txt文件的对象数据再次转化为Java对象(反序列化),代码如下:

public class SerializableTest { 
    /** 
     * 将类从文本中提取并赋值给内存中的类 
     */ 
    public static void readObj() { 
        try { 
            ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("/Users/guanliyuan/user.txt")); 
            try { 
                Object object = objectInputStream.readObject(); 
                User user = (User) object; 
                System.out.println(user); 
            } catch (ClassNotFoundException e) { 
                e.printStackTrace(); 
            } 
        } catch (IOException e) { 
            e.printStackTrace(); 
        } 
    } 
 
 
    public static void main(String args[]) { 
        readObj(); 
    } 
} 

通过反序列化操作,可以再次将持久化的对象字节流数据通过IO转化为Java对象。

4、关于serialVersionUID
对于JVM来说,要进行持久化的类必须要有一个标记,只有持有这个标记JVM才允许类创建的对象可以通过其IO系统转换为字节数据,从而实现持久化,而这个标记就是Serializable接口。而在反序列化的过程中则需要使用serialVersionUID来确定由那个类来加载这个对象,所以我们在实现Serializable接口的时候,一般还会要去尽量显示地定义serialVersionUID,如:

private static final long serialVersionUID = 1L; 

在反序列化的过程中,如果接收方为对象加载了一个类,如果该对象的serialVersionUID与对应持久化时的类不同,那么反序列化的过程中将会导致InvalidClassException异常。例如,在之前反序列化的例子中,我们故意将User类的serialVersionUID改为2L,如:

private static final long serialVersionUID = 2L; 

那么此时,在反序例化时就会导致异常,如下:

java.io.InvalidClassException: cn.wudimanong.serializable.User; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2 
    at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:687) 
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1880) 
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1746) 
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2037) 
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1568) 
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:428) 
    at cn.wudimanong.serializable.SerializableTest.readObj(SerializableTest.java:31) 
    at cn.wudimanong.serializable.SerializableTest.main(SerializableTest.java:44) 

如果我们在序列化中没有显示地声明serialVersionUID,则序列化运行时将会根据该类的各个方面计算该类默认的serialVersionUID值。但是,Java官方强烈建议所有要序列化的类都显示地声明serialVersionUID字段,因为如果高度依赖于JVM默认生成serialVersionUID,可能会导致其与编译器的实现细节耦合,这样可能会导致在反序列化的过程中发生意外的InvalidClassException异常。因此,为了保证跨不同Java编译器实现的serialVersionUID值的一致,实现Serializable接口的必须显示地声明serialVersionUID字段。

此外serialVersionUID字段地声明要尽可能使用private关键字修饰,这是因为该字段的声明只适用于声明的类,该字段作为成员变量被子类继承是没有用处的!有个特殊的地方需要注意的是,数组类是不能显示地声明serialVersionUID的,因为它们始终具有默认计算的值,不过数组类反序列化过程中也是放弃了匹配serialVersionUID值的要求。

最后

以上就是魁梧月饼为你收集整理的Java Serializable接口(序列化)的全部内容,希望文章能够帮你解决Java Serializable接口(序列化)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(50)

评论列表共有 0 条评论

立即
投稿
返回
顶部