java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > java序列化和反序列化

浅析关于java的序列化和反序列化

作者:yasooooo

这篇文章主要介绍了浅析关于java的序列化和反序列化,所谓序列化,就是把要传输的对象以及相关信息转换成字节数组进行存储的过程,而反序列化就是将字节数组再转回对象的过程,需要的朋友可以参考下

对于序列化和反序列化,大家或多或少都会听过一点。

所谓序列化,就是把要传输的对象以及相关信息转换成字节数组进行存储的过程。

而反序列化就是将字节数组再转回对象的过程。

对于序列化和反序列化总结了几点需要注意的地方,

1、实现Serializable接口的类才能够序列化,如果是父类实现了该接口,子类也可以进行序列化

这点不过多解释,规定就是这样。

2、静态成员不能被序列化、方法不能被序列化,关键字transient修饰的属性不能序列化

话不多说,上代码。

import java.io.*;
/**
 * 测试静态成员、方法、transient修饰的属性不能序列化
 */
public class SerializeTest {
    public static void main(String[] args) throws IOException {
        Test test = new Test(); // 创建被序列化的对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.data"));
        oos.writeObject(test);
        oos.close(); //这里记得序列化完成之后要关流,和io流一样,不多解释
    }
}
class Test implements Serializable{ //创建一个类用于创建序列化对象
    static int a;
    transient private int b;
    private int c;
    public void m(){}
}

以上代码就是创建了一个类,然后对该类的对象进行序列化,序列化后的文件为test.data.然后查看了一个这个文件的大小为53字节。

下面我将这个类中的静态成员、transient修饰的成员和m()方法都注释掉再进行一次序列化

import java.io.*;
/**
 * 测试静态成员、方法、transient修饰的属性不能序列化
 */
public class SerializeTest {
    public static void main(String[] args) throws IOException {
        Test test = new Test(); // 创建被序列化的对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.data"));
        oos.writeObject(test);
        oos.close(); //这里记得序列化完成之后要关流,和io流一样,不多解释
    }
}
class Test implements Serializable{ // 将static属性、transient属性和方法都注释掉之后在进行一次序列化
//    static int a;
//    transient private int b;
    private int c;
//    public void m(){}
}

此时,看到再序列化之后的文件大小依然还是53字节,但是如果再将成员变量c注释掉再进行序列化就会发现,这个文件变小了

由此可以说明,静态成员、transient修饰的成员和方法都不能序列化。

3、对于向上造型的对象进行序列化,实际序列化的对象是实际创建类

上代码

 
import java.io.*;
/**
 * 测试向上造型的对象实际序列化的是实际创建类
 */
public class SerializeTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Test test = new Test02();
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test02.data"));
        oos.writeObject(test);
        oos.close();
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test02.data"));
        System.out.println(ois.readObject()); //这里直接打印反序列化之后的对象信息
        ois.close();
    }
}
class Test implements Serializable{ //创建一个父类类用于创建序列化对象
}
class Test02 extends Test{    // 子类继承父类
}

上面代码的执行结果:Test02@4f3f5b24,可以看到打印的就是子类的对象,so,这个就证明了向上造型的对象,实际序列化的是实际创建类,同时也证明了如果父类实现了Serializable接口,子类也是可以序列化滴。

4、对于已经序列化完的对象,在进行反序列化时,如果对原类信息进行了修改(包含不能被序列化的属性和方法),此时将不能进行反序列化

直接上代码。

 
import java.io.*;
/**
 * 测试反序列化之前如果修改原类信息将不能进行反序列化
 * 
 */
public class SerializeTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test02.data"));
        System.out.println(ois.readObject()); //这里直接打印反序列化之后的对象信息
        ois.close();
    }
}
class Test implements Serializable{ //创建一个父类类用于创建序列化对象
}
class Test02 extends Test{    // 子类继承父类
    // test02.data这个文件就是第三条中创建对象序列化之后的文件
    static int a = 1;    //这里我随便添加了一个静态属性,然后执行代码
}

///下面是执行结果
Exception in thread "main" java.io.InvalidClassException: night.Test02; local class incompatible: stream classdesc serialVersionUID = -4461189770521473347, local class serialVersionUID = -7303293535763263875
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1829)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1713)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1986)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1535)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:422)
    at night.SerializeTest.main(SerializeTest.java:23)

居然报了异常?

这里为什么会报异常呢,简单讲一哈:序列化的时候会计算一个序列化版本号-----serialVersionUID,并且将这个版本号一同存放到文件中,在反序列化的时候会获取此序列号,并且会计算当前类的serialVersionUID,如果两个版本号不相等就会报如上异常,反之反序列化就可以成功。~

那么对于这种情况就没有解决办法了? 当然是有的。

我们可以在类中直接声明serialVersionUID为 final属性,并让其自动赋初始值(这个值jvm肯定是计算过的了),格式如下:

private static final long serialVersionUID = 362498820763181265L;

这样不管怎样修改类的属性,这个serialVersionUID都不会改变,这样问题就解决了。

其实对于private static final long serialVersionUID = 362498820763181265L;这个东西,大家应该都见过的,比如我这里拿的就是HashMap类源码中的那个值。

以上就是浅析关于java的序列化和反序列化的详细内容,更多关于java序列化和反序列化的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文