java Serialization 相关问题

注:如果您发现任何不正确的内容,或者您想要分享有关本文主题的更多信息,请撰写评论或联系我 15340938921@163.com。

以下整理文章参考《java编程思想第四版》《EffectiveJava》oracle java官网 以及部分博客

以下环境基于 windows 10 java version “1.8.0_171”

文中代码可能不太严谨,直供研究测试使用!

http://zjlgd.cn 请关注我的网站,大家一起进步!

是什么

​ java的序列化是java I/O流内容的一部分。

​ 序列化是将类的实例(类的对象)转换为字节流的过程。然后,此字节流可以作为文件存储在磁盘上,也可以通过网络发送到另一台计算机(使用另一台JVM 实例化的对象)。

序列化对象时 仅会序列化 类的默认可序列化字段被定义为非瞬态和非静态字段。 也就是非 static以及非transient 修饰字段

​ 当程序关闭或者休眠时,使用 Serialization 将对象状态保存在磁盘的某个文件上,当程序重新启动时可以访问该文件将对象重新new出来,这个过程可以称之为 反序列化。(术语可能不太标准,仅供理解)

当一个类从序列化状态恢复时,反序列化会绕过构造函数包括无参构造。

为什么

从何而来?

​ java的对象必须在内存在才能工作,一旦断电或者java程序中断,java产生的所有对象都烟消云散。

那么为什么不能跳过内存,直接使用硬盘来操作对象?

1.  按照 冯·诺依曼体系要求,数据必须在内存中,cpu才能操作。
2.  磁盘速度相比内存和cpu是非常慢的。

json不是一样可以达到持久性的效果吗,为什么要用java serializable?

​ 将对象的值 转为 json类型格式存储在文件,或者将对象值存储与数据库同样可以达到 java 序列化,持久性的效果。java的序列化看你的选择,功能就摆在那里,用不用是你的事。用java自己写的当然更加坚挺,持久!

​ 但是在使万物都成为对象的精神中,如果能够将一个对象声明为是“持久性”的,并为我们处理掉所有细节,那将会显得十分方便。

---- 摘自《java编程思想第四版》 18.12

  • java序列话可以将对象转化为字节序列,且这个序列可以恢复为原来的对象。在windows 实例化 的对象通过序列化将字节序列发给Unix系统,不必担心数据在不同机器的不同表示,以及字节顺序等等,java反序列化能够准确的重新组装。(支持RMI)
  • 利用java的序列化可以实现轻量级的持久性

还有等等·····

怎么样

注意:

如果父类已实现Serializable接口,则子类不需要实现它,反之亦然。

仅通过序列化过程保存非静态数据成员(not static)

静态数据成员和临时数据成员(transient)不通过序列化过程保存。因此,如果您不想保存非静态数据成员的值,则将其设置为瞬态。

反序列化对象时,永远不会调用对象的构造函数。

关联对象必须实现Serializable接口。

import java.io.Serializable;

public class Box implements Serializable {

    private static final long serialVersionUID = 61485867982808238L;

    private int width;
    private int height;

    public Box(int width, int height) {
        this.width = width;
        this.height = height;
    }

    public Box() {
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }
    
    // 为了方便观察结果,重写toString方法
    @Override
    public String toString() {
        return "Box{" +
                "width=" + width +
                ", height=" + height +
                '}';
    }
}
  • serialVersionUID 流的唯一标识符(序列版本) 适用于java序列化机制。简单来说,JAVA序列化的机制是通过判断类的serialVersionUID来验证的版本一致的。
  • 在进行反序列化时,JVM会把传来的字节流中的serialVersionUID于本地相应实体类的serialVersionUID进行比较。如果相同说明是一致的,可以进行反序列化,否则会出现反序列化版本一致的异常,即是InvalidCastException。

我可以不写serialVersionUID吗?

建议是 显式声明serialVersionUID。并且不为 1L

如果没有显式的声明私有的,静态的,最终的,long类型的serialVersionUID ,系统会自动根据这个类的相关数据,运行时生成标识。如果类的名称或成员变量,实现继承类有改变自动生成的标识也会改变。为了避免这方面的问题,应该显式声明serialVersionUID,并且他应该是唯一的,而不是 1L

取自 《Effective Java》第二版

关于 版本id 不同导致的详细情况 请参考 https://blog.csdn.net/u014750606/article/details/80040130

A 序列化 B 反序列化

  • 版本号不一致 —> 报错 InvalidCastException
  • 版本号一致,A 多字段 —> A多 字段丢失
  • 版本号一致,B 少字段 —> A多 字段丢失.
  • 版本号一致,B 多字段 —> B 多字段被赋予默认值

serializable序列化

​ serializable 接口是标记接口。这意味着如果您的类派生自此接口,则不必实现任何方法。这只是一个标记,Java运行时,在尝试序列化类时,只会检查类中是否存在此接口。如果类继承层次结构中存在Serializable接口,则Java运行时将负责类的序列化。 序列化我们需要执行以下几个步骤

1. 创建一个文件的新 FileOutputStream,我们想要序列化该类到指定的文件
2. 创建 ObjectOutputStream 有参构造入参为 第一步创建的FileOutputStream
3. 将对象写入 ObjectOutputStream
4. 关闭所有流
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;


public class Test {

    public static void SerializeToFile(Object classObject, String fileName) {
        try {
        // 这里的FileOutputStream 只是为了方便观察,将序列化后的结构写入文件。
            FileOutputStream fileStream = new FileOutputStream(fileName);
		// ObjectOutputStream继承OutputStream,序列化基于字节因要使用InputStream/OutputStream继承		 // 层次结构
            ObjectOutputStream objectStream = new ObjectOutputStream(fileStream);

            objectStream.writeObject(classObject);

            objectStream.close();
            fileStream.close();

        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Box rect = new Box(18, 78);
        // 我是windows 10 环境
        SerializeToFile(rect, "G:\\rectSerialized");

    }
}

执行下面这步将会将对象序列化

objectStream.writeObject(classObject);

在执行writeObject方法时,会校验要序列化的类是否标记serializable,如果不是将会抛出 NotSerializableException。

// remaining cases
if (obj instanceof String) {
	writeString((String) obj, unshared);
} else if (cl.isArray()) {
	writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
	writeEnum((Enum<?>) obj, desc, unshared);
} else if (obj instanceof Serializable) {
	writeOrdinaryObject(obj, desc, unshared);
} else {
	if (extendedDebugInfo) {
		throw new NotSerializableException(
		cl.getName() + "\n" + debugInfoStack.toString());
	} else {
		throw new NotSerializableException(cl.getName());
	}
}

对象的序列化会对对象内包含的引用进行跟踪,并且保存他。

执行main函数之后,在我的G盘根目录下生成了一个 rectSerialized 文件,大小为1KB,我尝试使用notepad++打开,但是显示的内容看不懂。。。

注意:

序列化只是序列化对象的变量(可序列化的),并不涉及方法

serializable反序列化

​ 反序列化即:将InputStream封装在ObjectInputStream中,调用readObject(),得到的是一个向上转型的 Object,需要向下转型 强转为指定对象。

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;


public class Test {

    public static Object DeSerializeFromFileToObject(String fileName) {
        try {

            FileInputStream fileStream = new FileInputStream(new File(fileName));

            ObjectInputStream objectStream = new ObjectInputStream(fileStream);

            Object deserializeObject = objectStream.readObject();

            objectStream.close();
            fileStream.close();

            return deserializeObject;

        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) {
        Box example =(Box)DeSerializeFromFileToObject("G:\\rectSerialized");
        System.out.println(example.toString());
    }
}

执行main函数结果如下:

Box{width=18, height=78}

Externalizable

序列化时会将本对象的某个子对象的某一部分进行序列化,这种情况可以实现Externalizable 接口。用法请自行参考。

谁用了Externalizable

哪个东西在哪里使用了序列化

TODO

  1. 为啥,数据必须在内存中,cpu才能操作。Why can’t CPUs get data directly from Hard drives?

参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值