关于java序列化,入门一篇即可,包会,包懂

本文详细介绍了Java序列化和反序列化的概念,包括将对象状态持久化、网络传输和RMI中的应用。通过实例展示了序列化过程,指出static和transient变量不会被序列化。强调了serialVersionUID的重要性,它用于确保序列化版本的一致性,避免因类结构变化导致的反序列化异常。同时,文章提供了如何设置serialVersionUID的方法,并提及成员变量也需要实现序列化接口。最后,讨论了类中包含其他类成员时的序列化要求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引入:序列化的三个小问题

  • 啥叫序列化和反序列化?
  • 为什么要序列化呢?
  • 为什么类中有serialVersionUID这样一个值,他体现的作用在哪里

无数次被序列化所困惑,于是今天好好进行整理,写在前面,这个Serializable接口,以及相关的东西,全部都在 Java io 里面的,这点是要额外注意下的。

1-1 定义

  • 序列化:把对象转换为字节序列的过程称为对象的序列化。
  • 反序列化:把字节序列恢复为对象的过程称为对象的反序列化。

通俗来说,序列化就是将一个对象中的有用的信息,进行持久化,持久化就是说存到硬盘里面,而不是在内存中。所以,序列化的目的还是在于将对象的状态放在一个地方,不管是文件也好,还是网络传递也好。

1-2 几种常见的需要序列化的场景

  • 把内存中的对象的状态持久化到一个对象或者是数据库中
  • 在网络上传输对象的时候
  • RMI(Remote Method Invocation)远程方法调用的时候

我自己只玩过第一个,第三个远程方法调用,我觉得会在我现在学的分布式微服务中得以体现,值得期待。

1-3 Demo(持久化到文件和库中)

  • Person.java
package 序列化;

import java.io.Serializable;

public class Person implements Serializable {

    private static final long serialVersionUID = -5090861933306306871L;

    private String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}
  • WriteObj2File.java
package 序列化;

import com.alibaba.fastjson.JSON;

import java.io.*;

public class WriteObj2File {

    public static void writeObj(String fileName) {
        try {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fileName));
            Person person = new Person("xx");
            oos.writeObject(person);
            oos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void readObj(String fileName) {
        try {
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName));
            Person p = (Person) ois.readObject();
            System.out.println(p.getName());
            ois.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * JSON序列化存库测试
     */
    public static void jsonTest() {
        Person person = new Person("xx");
        String jsonString = JSON.toJSONString(person);
        System.out.println(jsonString);
        // 存入库中 TODO

        // 读库 TODO
        Person person1 = JSON.parseObject(jsonString, Person.class);
        String person1Name = person1.getName();
        System.out.println(person1Name);

    }

    public static void main(String[] args) {
//        String fileName = "${填写你自己的路径}";
//        writeObj(fileName);
//        readObj(fileName);
        jsonTest();
    }
}
  • 小解释: 这里的代码都比较简单,搜索了资料,正常来说,存db里json字符串比较合适,我也没有实际用过,所以只是作为参考。存文件的是经典demo,没有问题的。也很好测试。

2-1 序列化经典小问题

2-1-1 不是类中所有的属性都可以被持久化的

声明为 static 和 transient 类型的成员变量不能被序列化。因为 static 代表类的状态,transient 代表对象的临时数据。

首先是transient

Person2.java

package 序列化;

import java.io.Serializable;

public class Person2 implements Serializable {

    private static final long serialVersionUID = 2952953958174783379L;

    private String name;
    private transient int age;
    private static String classData = "a";

    public Person2(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 static String getClassData() {
        return classData;
    }

    public static void setClassData(String classData) {
        Person2.classData = classData;
    }

    @Override
    public String toString() {
        return "Person2{" +
                "name='" + name + '\'' +
                ", classData='" + classData + '\'' +
                ", age=" + age +
                '}';
    }
}
  • Test.java
package 序列化;

import java.io.*;

public class Test {
    public static void writeObj(String fileName) {
        try {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fileName));
            Person2 person = new Person2("xx", 12);
            oos.writeObject(person);
            oos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void readObj(String fileName) {
        try {
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName));
            Person2 p = (Person2) ois.readObject();
            System.out.println(p);
            ois.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        String fileName = "/Users/xiaoxin/各类项目/java/java_project/basicLearning/src/main/java/序列化/person2.txt";
        writeObj(fileName);
        readObj(fileName);
    }
}

----output----
Person2{name='xx', classData='a', age=0}

可以看出,这里的transient age,并不是我们想象中的12,而是0,所以序列化是失败的,也就是说,带transient这个关键字的属性是无法序列化的。

下面来测试静态变量static,先把静态变量的值改成b,同时,只执行反序列化操作,看是否会将静态属性变回a。

private static String classData = "b";

public static void main(String[] args) {
 String fileName = "/Users/xiaoxin/各类项目/java/java_project/basicLearning/src/main/java/序列化/person2.txt";
 readObj(fileName);
}

----output----
Person2{name='xx', classData='b', age=0}

分析:可以比较明显看出,我们文件里面classData是a,这边确显示的是b,也就是说,这个属性并没有序列化成功。所以静态变量也不会序列化成功的。

2-1-2 为什么类中有serialVersionUID这样一个值,他体现的作用在哪里?

序列化接口是一定要的,这个没啥好质疑的,因为有的方法里面的参数对象,就必须实现这个接口。

我们先把序列化ID注释掉,来进行一次序列化和反序列化操作。

package 序列化;

import java.io.Serializable;

public class Person2 implements Serializable {

//    private static final long serialVersionUID = 2952953958174783379L;

    private String name;
    private transient int age;

    public Person2(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;
    }

    @Override
    public String toString() {
        return "Person2{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public static void main(String[] args) {
  String fileName = "/Users/xiaoxin/各类项目/java/java_project/basicLearning/src/main/java/序列化/person2.txt";
  writeObj(fileName);
  readObj(fileName);
}

无事发生,一切正常。那么如果这个时候,我自己突然加了一个属性呢?

package 序列化;

import java.io.Serializable;

public class Person2 implements Serializable {

//    private static final long serialVersionUID = 2952953958174783379L;

    private String name;
    private transient int age;
    private double height;

    public Person2(String name, int age, double height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }


    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 double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

    @Override
    public String toString() {
        return "Person2{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", height=" + height +
                '}';
    }
}

public static void main(String[] args) {
  String fileName = "/Users/xiaoxin/各类项目/java/java_project/basicLearning/src/main/java/序列化/person2.txt";
  //        writeObj(fileName);
  readObj(fileName);
}


----output----
  java.io.InvalidClassException: 序列化.Person2; local class incompatible: stream classdesc serialVersionUID = 7030615533941445544, local class serialVersionUID = -7417946223875850957
	at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699)
	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2003)
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1850)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2160)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1667)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:503)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:461)
	at 序列化.Test.readObj(Test.java:20)
	at 序列化.Test.main(Test.java:31)

分析:这边直接显示序列化ID不对了,为什么呢?我们类里面是没有手动定义这个serialVersionUID的那么,系统会给我们根据属性自动生成一个,我们第一次序列化的时候,进入本地是一个serialVersionUID,后面我们添加了一个属性,系统给我们生成了另一个serialVersionUID,很明显,这两个serialVersionUID,一定是不相同的。现在我们再手动加回自己的serialVersionUID,看效果。就会发现一切都正常了。所以,由此可见,serialVersionUID的作用就很明显了。这个id一旦定下来,就绝对不能改,也必须要自己添加好,不然碰到序列化和反序列化的时候,这个问题一定会出来。

2-1-3 如何设置serialVersionUID?

Idea 可以帮你搞定,具体方法看下面这个链接。https://blog.csdn.net/wd2014610/article/details/100735074,别人的,就当是帮他引流了哈哈哈哈。

2-1-4 如果成员变量也是一个类呢?

注意,成员变量是一个类的话,这个类也要实现序列化接口的,不然会报错java.io.NotSerializableException:

2-1-5 如果还有相关问题可以再进行补充,上面的是比较常见的

确实有参考,我做了点归纳,原作:https://blog.csdn.net/qq_27093465/article/details/78544505?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163289923216780271562162%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=163289923216780271562162&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_positive~default-1-78544505.pc_search_result_control_group&utm_term=%E5%BA%8F%E5%88%97%E5%8C%96&spm=1018.2226.3001.4187

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

河海哥yyds

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值