正规的手机网站建设,怎么购买云服务器,青浦建设机械网站,wordpress 自动保存图片目录 1. 序列化流与反序列化流的基本介绍
2. 序列化流的基本用法#xff1f;
3. 序列化流的作用#xff1f;
4. 反序列化流的基本用法#xff1f;
5. 反序列化流的作用
6. 序列化流与反序列化流使用时需要注意的细节#xff08;非常重要#xff09;
6.1 被序列化的…目录 1. 序列化流与反序列化流的基本介绍
2. 序列化流的基本用法
3. 序列化流的作用
4. 反序列化流的基本用法
5. 反序列化流的作用
6. 序列化流与反序列化流使用时需要注意的细节非常重要
6.1 被序列化的JavaBean类必须实现 Serializable 接口
6.2 javaBean类尽量在定义时加上版本号
6.3 transient 关键字的使用
6.4 序列化流得到的文件不要修改 1. 序列化流与反序列化流的基本介绍 如上图所示序列化流与反序列化流也是IO流中会用到的一种高级流也是用来包装基本流对象的它们的继承关系如上图中所示。
序列化流是字节流的一种它负责输出数据。
反序列化流也是字节流的一种它负责输入数据。 2. 序列化流的基本用法
如下图所示为序列化流的构造器序列化流也是一个高级流它的参数需要传递一个基本流对象
序列化流地写方法也很简单名为writeObject()参数需要传递一个对象如下所示 我来简单给大家演示一下它的使用
(1)既然它是用来写对象的我们就先创建一个JavaBean类如下我定义了一个银行账户类
// 这里面要记住一定要记得实现 Serializable 接口否则会序列化失败
public class Account implements Serializable {// 账户idprivate Integer accountId;// 账户人名称private String accountName;// 账户密码private String accountPassword;Overridepublic String toString() {return Account{ accountId accountId , accountName accountName \ , accountPassword accountPassword \ };}public Integer getAccountId() {return accountId;}public void setAccountId(Integer accountId) {this.accountId accountId;}public String getAccountName() {return accountName;}public void setAccountName(String accountName) {this.accountName accountName;}public String getAccountPassword() {return accountPassword;}public void setAccountPassword(String accountPassword) {this.accountPassword accountPassword;}
}
接着我们来到另一个测试类创建一个 main 方法测试序列化流的使用我把注释也标注进去
public static void main(String[] args) {// 定义一个账户对象并赋予初始值Account account new Account(101,张三,123456);// 创建一个 ObjectOutputStream 的流对象ObjectOutputStream oos null;try {// 对 oos 赋值// 将 account 的而对象的结果写道 account.txt 文件中去oos new ObjectOutputStream(new FileOutputStream(user-service/account.txt));// 将 account 对象写出oos.writeObject(account);} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {// 保证给程序的健壮性关闭之前先做判空操作否则会空指针异常try {if (oos ! null)oos.close();} catch (IOException e) {e.printStackTrace();}}}
我们运行此main 方法在项目目录下会得到名为 account.txt 的文件里面就存放着我们 account 对象的信息我们打开查阅如下所示 可以看到我们打开所看到的是一大堆乱码这是为什么呢
其实这是正常现象序列化流可以把Java文件写到本地文件中去通常情况下写到本地文件的系列化流文件我们都是看不懂的因为这本来就不是给我们看的而是用于给程序看的。下面我们就此来引出序列化流的作用。 3. 序列化流的作用
通过刚才的一个小例子我们简单了解了序列化流的使用也留下了一个问题。既然写出的文件看不懂我们要他来做什么用呢
怎么说呢我举个最简单的例子假如你电脑上有一个单机游戏比如植物大战僵尸吧大家不知道玩过没有里面就有金币对吧当我们不想玩了之后关闭游戏游戏是会把我们的金币数额和所有的通关进度通过IO流的方式写到我们的本地磁盘上去的当我们再次打开要玩的时候它会再次读取到本地文件中的内容将我们的金币数额和通关进度在读到内存中我们就可以接着上次的进度游玩了。
游戏将金币数额与游戏进度保存的这个过程就和序列化很相似
游戏会将金币数额和游戏进度再次读取到内存中去就和反序列化有些相似。
那么现在就有一个问题了如果你是这个游戏的设计者你在序列化的时候序列化的得到的游戏进度文件希望玩家能看懂能修改吗肯定不能吧如果任意玩家都可以看得懂并且能修改序列化得到的游戏进度文件那和开外挂有什么区别我直接把金币改成9999999999不用再打金币了。
这样举例子各位应该就有些明白了吧
这也只是序列化流的用途之一序列化流与反序列化流的用途还有很多比如互联网传输用户数据我们通常都是采用二进制传输你总不能将数据原样进行传输吧特别是如果传输的数据带有敏感信息比如账户名称密码银行账户密码这些是绝对不能让用户看得懂可以修改的。一旦被不法人员拦截攻击造成的损失会非常大因此就可以使用我们的序列化流将要传输的数据进行序列化处理。 4. 反序列化流的基本用法
如上图所示即为反序列化流的构造方法与常用方法我们来简单的测试一下吧
如下代码我们把刚才序列化的 account 对象在反序列化回来我们试一试
public static void main(String[] args) {// 定义一个账户对象并赋予初始值Account account new Account(101,张三,123456);// 创建一个 ObjectOutputStream 的流对象ObjectInputStream ois null;try {// 对 ois 赋值// 将 account.txt 文件中保存的对象数据再读出来ois new ObjectInputStream(new FileInputStream(user-service/account.txt));// 将 account 对象读出Object o ois.readObject();// 输出独到的 account 对象数据System.out.println(o);} catch (IOException | ClassNotFoundException e) {e.printStackTrace();} finally {// 保证给程序的健壮性关闭之前先做判空操作否则会空指针异常try {if (ois ! null)ois.close();} catch (IOException e) {e.printStackTrace();}}} 运行 main 方法就可以得到如下图所示的结果 可以看到原本我们创建的对象 account 就又被我们读取出来了而且还能还得懂。 5. 反序列化流的作用
通过刚才的演示与对序列化流作用的说明这里就不需要我在多做解释了总而言之就一句话。
反序列化流可以将序列化的数据载读取为我们可以看的懂可以操作的原本模样。 6. 序列化流与反序列化流使用时需要注意的细节非常重要
6.1 被序列化的JavaBean类必须实现 Serializable 接口
我们可以来看一下 Serializable 接口的源码如下所示 可以看到再序列化流接口中没有任何东西这种借口有一个专业名词被称为标记型接口备有实际含义只是作为一个标记只有被标记的JavaBean类才能参与序列化。如果不实现该接口就会报错这里应该也不用多解释各位在做 Spring 项目的时候通常都会在javaBean类中是实现 Serializable 接口因为我们项目运行时的数据就是在互联网之间进行传输的所以需要实现 Serializable 接口。
6.2 javaBean类尽量在定义时加上版本号
有些小伙伴可能不知道当你对一个JavaBean类添加了 Serializable 接口要参与序列化时该JavaBean类是有版本号的如果你对javaBean类做了修改版本号也会修改一旦版本号修改再序列化与反序列化时就会出现错误因此我们最好在JavaBean类中添加上版本号这个我们可以借鉴 ArrayList 的源码设计哦我们来看一下 在ArrayList 源码中我们可以发现它也定义了一个版本号而且各位要注意这个版本号变量不是乱起的必须是 serialVersionUID不能是别的如果不添加 serialVersionUID 变量我们的javaBean类一旦发生修改版本号也会修改就是因为版本号不一致导致无法序列化成功因此我们需要让版本号使用为一个就需要定义为 final又因为它是该类属性中共享属性需要定义为 static serialVersionUID 最好定义为 long 类型。
因此在定义版本号时最好定义为
private static final long serialVersionUID
至于取什么值无所谓只要遵守该变量的定义原则即可。
6.3 transient 关键字的使用
还有一些时候我们不想让JavaBean 类中的一些变量参与序列化与反序列化该怎么办呢
这个时候就可以用到我们的 transient 关键字。
被 transient 关键字修饰的变量不会参与到类的序列化与反序列化我来给大家演示一下还拿刚才的例子举例说明如下图所示 我把 accountpassword 账户密码这一属性不参与数列化因为太不安全我们就可以在JavaBean类中 该变量前面加上 transient 关键字一旦加上就不会参与序列化我们再回到 main 方法作出修改
public static void main(String[] args) {// 定义一个账户对象并赋予初始值Account account new Account(101,张三,123456);// 创建一个 ObjectOutputStream 的流对象ObjectOutputStream oos null;// 创建一个 ObjectOutputStream 的流对象ObjectInputStream ois null;try {// 对 oos 赋值oos new ObjectOutputStream(new FileOutputStream(user-service/account.txt));// 对 ois 赋值// 将 account.txt 文件中保存的对象数据再读出来ois new ObjectInputStream(new FileInputStream(user-service/account.txt));// 先将 account 对象写出oos.writeObject(account);// 再将 account 对象读出Object o ois.readObject();// 输出独到的 account 对象数据System.out.println(o);} catch (IOException | ClassNotFoundException e) {e.printStackTrace();} finally {// 保证给程序的健壮性关闭之前先做判空操作否则会空指针异常try {if (oos ! null)oos.close();if (ois ! null)ois.close();} catch (IOException e) {e.printStackTrace();}}} 然后再次运行得到如下结果 可以发现即便我们初始为 accountPassword 赋值但因为添加了 transient 关键字的缘故他就不会参与序列化与反序列化系统胡自动给一个默认值为 null。
各位小伙伴有些时候再看源码时的时候可能也见过 transient 关键字就是用来做标记的不让其被标记的变量参与序列化。
6.4 序列化流得到的文件不要修改
我们在通过序列化得到的文件虽然是一堆乱码看不懂但时不要轻易去修改因为一旦修改再次反序列化时程序就读不出来了会报错这也是序列化流中需要注意的一个点哦