Java Serializable 序列化 与 对象克隆
发布日期:2021-06-23 19:02:32 浏览次数:12 分类:技术文章

本文共 7327 字,大约阅读时间需要 24 分钟。

目录


序列化:即提取数据对象并将其转换为字节流(二进制格式)的过程,因此它可以通过网络传输或保存在数据库、以及本地文件中,后续可以再反序列化为对象。

实现 Serializable 接口

1、使用默认的序列化机制,即实现 Serializable 接口即可,不需要实现任何方法。

2、Serializable 接口没有任何方法,只是一个标记而已,告诉Java虚拟机该类可以被序列化了。利用 ObjectOutputStream 的writeObject(object) 方法进行序列化,用 ObjectInputStream 的 readObject() 方法进行反序列化。

3、该方式下序列化机制会自动保存该对象的成员变量,static 成员变量和 transient 关键字修饰的成员变量不会被序列化保存

准备 User 实体类如下:

import java.io.Serializable;import java.util.Date;/** * Created by Administrator on 2018/7/31 0031. * 用户类----实现 Serializable 接口 */public class User implements Serializable {    private static final long serialVersionUID = 1749243243491717954L;//具体数值自己定义    private Integer id;    private String name;    private Date birthday;    private static String address = "China";//默认情况下,静态属性不会被序列化    public Date getBirthday() {        return birthday;    }    public void setBirthday(Date birthday) {        this.birthday = birthday;    }    public Integer getId() {        return id;    }    public void setId(Integer id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public static String getAddress() {        return address;    }    public static void setAddress(String address) {        User.address = address;    }    @Override    public String toString() {        return "User{" +                "id=" + id +                ", name='" + name + '\'' +                ", birthday=" + birthday +                '}';    }}

序列化如下:

import java.io.*;import java.util.Date;/** * Created by Administrator on 2018/7/25 0025. */public class Test {    public static void main(String[] args) {        User user = new User();        user.setId(9527);        user.setName("华安");        user.setBirthday(new Date());        User.setAddress("USA");        String path = "D:/abc.txt";//序列化保存在本地的位置        new Test().serialize(user, path);    }    /**     * 将对象序列化到指定文件     *     * @param user     * @param path     */    public void serialize(User user, String path) {        try {            FileOutputStream fileOutputStream = new FileOutputStream(new File(path));            ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);            objectOutputStream.writeObject(user);            objectOutputStream.flush();            objectOutputStream.close();        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }    }}

代码运行后,就会在 D:/abc.txt 文件中保存好序列化后的数据。

反序列化:

import java.io.*;/** * Created by Administrator on 2018/7/25 0025. */public class Test {    public static void main(String[] args) {        String path = "D:/abc.txt";//序列化保存在本地的位置        new Test().deserialize(path);    }    /**     * 从文件反序列化     *     * @param path     */    public void deserialize(String path) {        try {            FileInputStream fileInputStream = new FileInputStream(new File(path));            ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);            Object user = objectInputStream.readObject();            objectInputStream.close();            System.out.println("反序列化结果 user :" + user);            System.out.println("反序列化结果 User.getAddress():" + User.getAddress());        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        } catch (ClassNotFoundException e) {            e.printStackTrace();        }    }}

控制台输出如下:

反序列化结果 user :User{id=9527, name='华安', birthday=Wed Jul 10 14:46:09 CST 2019}

反序列化结果 User.getAddress():China

serialVersionUID 版本号

1、序列化后,如上 User 所示,其中通常都会一行代码:private static final long serialVersionUID = 1749243243491717954L;

2、假设类 A 实现 Serializable 接口,当使用序列化将对象传输到了其它地方,此时因为一些原因修改了类 A(比如添加属性、删除属性、修改属性等),那么之前序列化后的数据能否再反序列化为对象呢?

1)如果类中自定义了 serialVersionUID 属性,且值未发生变化,则可以正确反序列化回来。

2)如果类中没有自定义 serialVersionUID 属性,则此时无法反序列化为对象,并报错如下:

java.io.InvalidClassException: wmx.com.springboothello.test.User; local class incompatible: stream classdesc serialVersionUID = -4467989014877592694, local class serialVersionUID = -7572413328418759383

3、假设类 B 实现 Serializable 接口,类中没有自定义 serialVersionUID 属性,则序列化时会根据算法得到一个默认值,之后只要类 B 没有做修改,则这个默认的 serialVersionUID 值会一直不变,反序列化也就不会有问题。如果之后类 B 发生了变化,则此时它的 serialVersionUID 也会发生变化,旧的 serialVersionUID 值反序列化时就会因为值不匹配新值而发生如上错误。

4、同理假如类 C 实现 Serializable 接口,类中自定义了 serialVersionUID 属性值,后面如果修改了类 C ,但只要 serialVersionUID 属性值不变,则之前序列化的内容仍然可以正常反序列化。如果此时不想再让以前序列化的内容能反序列化成功,则只需要手动修改 serialVersionUID 属性值即可。

5、serialVersionUID 属性值可以自己随意指定,但是名称必须写死,如果写成 suid、seriaUID等等都是无效的。

IDEA 快捷键生成 serialVersionUID

1、打开 IDEA setting 窗口后直接搜索 "serialVersionUID",然后将 serializable class without ‘serialVersionUID’ 勾选上

2、需要生成 serialVersionUID  的类先实现 Serializable 接口

3、最后鼠标点击需要生成的类名,按下快捷键 alt+entry,即可选择进行生成。

序列化方式克隆对象

1、对于 Java Bean(POJO对象)可以使用 Spring BeanUtils、、 等库,

但是它们用的是反射机制,所以只对 Java bean 有效,而且 POJO 对象还必须提供 getter、setter 方法,而对 Map、List、Set 等数据结构无法复制。

2、所以对于项目中经常遇到的 Map、List、Set 等等数据结构,使用序列化的方式可以对其进行克隆。

import org.junit.Test;import java.io.*;import java.util.*;/** * Java 对象深度克隆 * * @author wangMaoXiong * @version 1.0 * @date 2020/7/31 16:39 */public class DeepCopyTest {    /**     * Map 对象深度复制     *     * @param src 被克隆的对象,如:Map
、Map
等等 * @param
* @param
:支持任意类型,如果是 java Bean,则必须实现 {@link Serializable} 接口实现序列化,否则报错 * @return * @throws IOException * @throws ClassNotFoundException */ public static
Map
deepCopy(Map
src) throws IOException, ClassNotFoundException { ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(byteOut); out.writeObject(src); ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray()); ObjectInputStream in = new ObjectInputStream(byteIn); Map
dest = (Map
) in.readObject(); return dest; } /** * List 对象深度复制 * * @param src :被克隆的对象,如:List
、List
>、List
等等 * @param
:支持任意类型,如果是 java Bean,则必须实现 {@link Serializable} 接口实现序列化,否则报错 * @return * @throws IOException * @throws ClassNotFoundException */ public static
List
deepCopy(List
src) throws IOException, ClassNotFoundException { ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(byteOut); out.writeObject(src); ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray()); ObjectInputStream in = new ObjectInputStream(byteIn); List
dest = (List
) in.readObject(); return dest; } /** * 对象深度克隆。使用 java 序列化的方式将对象进行克隆。 * * @param src :被克隆的对象,如 java bean,Set、List、Map 等等 * @param
:支持任意类型,如果是 java Bean,则必须实现 {@link Serializable} 接口实现序列化,否则报错 * @return */ public static
E cloneObject(E src) { Assert.isTrue(!ObjectUtils.isEmpty(src), "参数不能为空"); E dest = null; ObjectOutputStream out = null; try { ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); out = new ObjectOutputStream(byteOut); out.writeObject(src); ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray()); ObjectInputStream in = new ObjectInputStream(byteIn); dest = (E) in.readObject(); } catch (Exception e) { e.printStackTrace(); } finally { if (out != null) { try { out.flush(); out.close(); } catch (IOException e) { e.printStackTrace(); } } } return dest; }}

对象克隆在线测试源码:

 

转载地址:https://wangmaoxiong.blog.csdn.net/article/details/81295418 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Swift基础-0001
下一篇:【鸿蒙 HarmonyOS 快速入门·第一课】 HiSpark Wi-Fi IoT 套件基础入门

发表评论

最新留言

留言是一种美德,欢迎回访!
[***.207.175.100]2024年04月04日 16时53分12秒