死磕Java之序列化与反序列化

矫情吗;* 2022-03-17 12:12 297阅读 0赞

死磕Java之序列化与反序列化

d3hfZm10PXBuZw

当创建对象时,它就一直存在,但是在程序终止之后,无论如何它不会存在。如果现在的需求是当程序终止后,需要将信息保存起来,这就需要Java序列化了。

aHR0cHM6Ly9tbWJpei5xbG9nby5jbi9tbWJpel9wbmcvN1FSVHZrSzJxQzRiUHNLc2Zsa3lVSUdRQnR3YnJWT045em1DZG1XOUhuZnpkR21zNGVVSXpodzYwZXdtWTBySk5hZExZbFROZmRKVGhoSzBhZk4xdHcvMD93eF9mbXQ9cG5n

01

序列化的引入

d3hfZm10PXBuZw

Java序列化的引入主要基于两方面的考虑:

1.为了支持Java的RMI(远程过程调用),它使存活于其他计算机上的对象使用起来就像存在于本机计算机一样。

2.对于Java Beans来说,对象的序列化也是必要的。

aHR0cHM6Ly9tbWJpei5xbG9nby5jbi9tbWJpel9wbmcvN1FSVHZrSzJxQzRiUHNLc2Zsa3lVSUdRQnR3YnJWT045em1DZG1XOUhuZnpkR21zNGVVSXpodzYwZXdtWTBySk5hZExZbFROZmRKVGhoSzBhZk4xdHcvMD93eF9mbXQ9cG5n

02

序列化的定义

d3hfZm10PXBuZw

Java的序列化定义是将对象性转换为字节序列。实现Java序列化的主要方法有两种,一种是实现Serializable接口,还有一种是使用Externalizable接口。

与Java序列化相反的过程称之为Java反序列化,主要将字节序列转换为对象的过程。

aHR0cHM6Ly9tbWJpei5xbG9nby5jbi9tbWJpel9wbmcvN1FSVHZrSzJxQzRiUHNLc2Zsa3lVSUdRQnR3YnJWT045em1DZG1XOUhuZnpkR21zNGVVSXpodzYwZXdtWTBySk5hZExZbFROZmRKVGhoSzBhZk4xdHcvMD93eF9mbXQ9cG5n

03

序列化与反序列化的过程

d3hfZm10PXBuZw

要序列化一个对象,首先要创建某些OutputStream对象,然后将其封装在一个ObjectOutputStream对象内。这时,只需调用writeObject()即可将对象序列化,将其发送给OutputStream(对象化序列是基于字节的,因而使用InputStream和OutputStream继承层次结构)。要反向进行该过程(即将一个序列还原为一个对象),需要将一个InputStream封装在ObjectInputStream内部,然后调用readObject()方法。

aHR0cHM6Ly9tbWJpei5xbG9nby5jbi9tbWJpel9wbmcvN1FSVHZrSzJxQzRiUHNLc2Zsa3lVSUdRQnR3YnJWT045em1DZG1XOUhuZnpkR21zNGVVSXpodzYwZXdtWTBySk5hZExZbFROZmRKVGhoSzBhZk4xdHcvMD93eF9mbXQ9cG5n

04

Serializable接口实现

d3hfZm10PXBuZw

我们这里定义了一个Account类,Account类实现Serializable接口,下面是Java序列化过程如下:

d3hfZm10PXBuZw

  1. public class Account implements Serializable {
  2. private String username;
  3. private double balance;
  4. private String password;
  5. public Account() {
  6. }
  7. public Account(String username, double balance, String password) {
  8. this.username = username;
  9. this.balance = balance;
  10. this.password = password;
  11. }
  12. public String getUsername() {
  13. return username;
  14. }
  15. public double getBalance() {
  16. return balance;
  17. }
  18. public String getPassword() {
  19. return password;
  20. }
  21. public void setUsername(String username) {
  22. this.username = username;
  23. }
  24. public void setBalance(double balance) {
  25. this.balance = balance;
  26. }
  27. public void setPassword(String password) {
  28. this.password = password;
  29. }
  30. @Override
  31. public String toString() {
  32. return "Account{" +
  33. "username='" + username + '\'' +
  34. ", balance=" + balance +
  35. ", password='" + password + '\'' +
  36. '}';
  37. }
  38. public static void main(String[] args) throws IOException, ClassNotFoundException {
  39. Account account=new Account("Bob",343.44,"sdfdas");
  40. //程序猿技术
  41. ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("e:\\object.dat"));
  42. out.writeObject(account);
  43. }

aHR0cHM6Ly9tbWJpei5xbG9nby5jbi9tbWJpel9wbmcvN1FSVHZrSzJxQzRiUHNLc2Zsa3lVSUdRQnR3YnJWT045em1DZG1XOUhuZnpkR21zNGVVSXpodzYwZXdtWTBySk5hZExZbFROZmRKVGhoSzBhZk4xdHcvMD93eF9mbXQ9cG5n

Account的实例对象序列化到E盘下的object二进制文件中。反序列的过程代码如下:

d3hfZm10PXBuZw

  1. public static void main(String[] args) throws IOException, ClassNotFoundException { ObjectInputStream in=new ObjectInputStream(new FileInputStream("e:\\object.dat")); Account account=(Account)in.readObject(); System.out.println(account.getUsername()); }

运行结果如下:

image.png

aHR0cHM6Ly9tbWJpei5xbG9nby5jbi9tbWJpel9wbmcvN1FSVHZrSzJxQzRiUHNLc2Zsa3lVSUdRQnR3YnJWT045em1DZG1XOUhuZnpkR21zNGVVSXpodzYwZXdtWTBySk5hZExZbFROZmRKVGhoSzBhZk4xdHcvMD93eF9mbXQ9cG5n

不知道你是否注意到,对于反序列化是存在不安全的!如果从文件读取出来的数据反序列化成对象后,文件内含有恶意数据可能危害Java虚拟机。

在上面的代码中,实际生活中密码可能不需要序列化到文件中,每一个人都可以读取文件来获取对象,这样是非常不安全的。因而,如果我们可以选择序列化的字段,这样的序列化方式是非常可去的。下面我们将介绍这种序列化方式。

aHR0cHM6Ly9tbWJpei5xbG9nby5jbi9tbWJpel9wbmcvN1FSVHZrSzJxQzRiUHNLc2Zsa3lVSUdRQnR3YnJWT045em1DZG1XOUhuZnpkR21zNGVVSXpodzYwZXdtWTBySk5hZExZbFROZmRKVGhoSzBhZk4xdHcvMD93eF9mbXQ9cG5n

05

Externalizable接口实现

d3hfZm10PXBuZw

不同于Serializable接口,Externalizable接口提供了readExternal()方法和writeExternal()方法来可控的序列化对象的字段。上一节中Account类的序列化实现:

d3hfZm10PXBuZw

  1. public class Account implements Externalizable {
  2. private String username;
  3. private double balance;
  4. private String password;
  5. public Account() {}
  6. public Account(String username, double balance, String password) {
  7. this.username = username;
  8. this.balance = balance;
  9. this.password = password;
  10. }
  11. public String getUsername() {
  12. return username;
  13. }
  14. public double getBalance() {
  15. return balance;
  16. }
  17. public String getPassword() {
  18. return password;
  19. }
  20. public void setUsername(String username) {
  21. this.username = username;
  22. }
  23. public void setBalance(double balance) {
  24. this.balance = balance;
  25. }
  26. public void setPassword(String password) {
  27. this.password = password;
  28. }
  29. @Override
  30. public String toString() {
  31. return "Account{" +
  32. "username='" + username + '\'' +
  33. ", balance=" + balance +
  34. ", password='" + password + '\'' +
  35. '}';
  36. }
  37. public static void main(String[] args) throws IOException, ClassNotFoundException {
  38. Account account=new Account("Bob",343.44,"sdfdas");
  39. //程序猿技术
  40. ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("e:\\object.dat"));
  41. out.writeObject(account);
  42. }
  43. public void writeExternal(ObjectOutput out) throws IOException {
  44. out.writeUTF(username);
  45. out.writeDouble(balance);
  46. }
  47. public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
  48. in.readUTF();
  49. in.readDouble();
  50. }
  51. }

aHR0cHM6Ly9tbWJpei5xbG9nby5jbi9tbWJpel9wbmcvN1FSVHZrSzJxQzRiUHNLc2Zsa3lVSUdRQnR3YnJWT045em1DZG1XOUhuZnpkR21zNGVVSXpodzYwZXdtWTBySk5hZExZbFROZmRKVGhoSzBhZk4xdHcvMD93eF9mbXQ9cG5n

需要注意的是,两个方法的字段顺序要一致。

现在测试password字段是否序列化,反序列化的过程如下:

d3hfZm10PXBuZw

  1. public static void main(String[] args) throws IOException, ClassNotFoundException {
  2. //反序列化过程
  3. ObjectInputStream in=new ObjectInputStream(new FileInputStream("e:\\object.dat"));
  4. Account account=(Account)in.readObject();
  5. System.out.println("密码:"+account.getPassword());
  6. }

运行结果如下:

image.png

aHR0cHM6Ly9tbWJpei5xbG9nby5jbi9tbWJpel9wbmcvN1FSVHZrSzJxQzRiUHNLc2Zsa3lVSUdRQnR3YnJWT045em1DZG1XOUhuZnpkR21zNGVVSXpodzYwZXdtWTBySk5hZExZbFROZmRKVGhoSzBhZk4xdHcvMD93eF9mbXQ9cG5n

从结果可以看出,密码并没有被序列化。

aHR0cHM6Ly9tbWJpei5xbG9nby5jbi9tbWJpel9wbmcvN1FSVHZrSzJxQzRiUHNLc2Zsa3lVSUdRQnR3YnJWT045em1DZG1XOUhuZnpkR21zNGVVSXpodzYwZXdtWTBySk5hZExZbFROZmRKVGhoSzBhZk4xdHcvMD93eF9mbXQ9cG5n

06

transient关键字

d3hfZm10PXBuZw

可控的序列化还有一种方案,就是在不需要序列化的字段前,使用transient关键字进行修饰,表示将这个字段关闭序列化。代码如下:

d3hfZm10PXBuZw

  1. public class Account implements Serializable {
  2. private String username;
  3. private double balance;
  4. private transient String password;
  5. public Account() {
  6. }
  7. public Account(String username, double balance, String password) {
  8. this.username = username;
  9. this.balance = balance;
  10. this.password = password;
  11. }
  12. public String getUsername() {
  13. return username;
  14. }
  15. public double getBalance() {
  16. return balance;
  17. }
  18. public String getPassword() {
  19. return password;
  20. }
  21. public void setUsername(String username) {
  22. this.username = username;
  23. }
  24. public void setBalance(double balance) {
  25. this.balance = balance;
  26. }
  27. public void setPassword(String password) {
  28. this.password = password;
  29. }
  30. @Override
  31. public String toString() {
  32. return "Account{" +
  33. "username='" + username + '\'' +
  34. ", balance=" + balance +
  35. ", password='" + password + '\'' +
  36. '}';
  37. }
  38. public static void main(String[] args) throws IOException, ClassNotFoundException {
  39. Account account=new Account("Bob",343.44,"sdfdas");
  40. //程序猿技术
  41. ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("e:\\object.dat"));
  42. out.writeObject(account);
  43. //反序列化过程
  44. ObjectInputStream in=new ObjectInputStream(new FileInputStream("e:\\object.dat"));
  45. Account account1=(Account)in.readObject();
  46. System.out.println("密码:"+account1.getPassword());
  47. }
  48. }

运行的结果如下:

image.png

aHR0cHM6Ly9tbWJpei5xbG9nby5jbi9tbWJpel9wbmcvN1FSVHZrSzJxQzRiUHNLc2Zsa3lVSUdRQnR3YnJWT045em1DZG1XOUhuZnpkR21zNGVVSXpodzYwZXdtWTBySk5hZExZbFROZmRKVGhoSzBhZk4xdHcvMD93eF9mbXQ9cG5n

aHR0cHM6Ly9tbWJpei5xbG9nby5jbi9tbWJpel9wbmcvN1FSVHZrSzJxQzRiUHNLc2Zsa3lVSUdRQnR3YnJWT045em1DZG1XOUhuZnpkR21zNGVVSXpodzYwZXdtWTBySk5hZExZbFROZmRKVGhoSzBhZk4xdHcvMD93eF9mbXQ9cG5n

07

序列化的意义

d3hfZm10PXBuZw

利用序列化可以实现轻量级的持久化,这样可以让一个对象的生命周期并不取决于程序是否在进行;它可以存在于程序调用之间;通过将一个序列化对象写入磁盘,然后重新调用程序是恢复该对象,就能实现持久化。

aHR0cHM6Ly9tbWJpei5xbG9nby5jbi9tbWJpel9wbmcvN1FSVHZrSzJxQzRiUHNLc2Zsa3lVSUdRQnR3YnJWT045em1DZG1XOUhuZnpkR21zNGVVSXpodzYwZXdtWTBySk5hZExZbFROZmRKVGhoSzBhZk4xdHcvMD93eF9mbXQ9cG5n

5c6fde8c-16d0-4e65-9f67-5507ac10c663.png

aHR0cHM6Ly9tbWJpei5xbG9nby5jbi9tbWJpel9naWYvdU4xTElhdjdvSmlieVN6VTRLVWRBSG1jWUpBUnRSUzlmQTY5Q2xGc3lzZzllNWlhc2gxNmFrYlpqdk1XdExRRkNvUldmeEpjRWdzdFJTT0NRaWN0eDJsckEvMD93eF9mbXQ9Z2lm

点击上方二维码,关注我们

aHR0cHM6Ly9tbWJpei5xbG9nby5jbi9tbWJpel9naWYvdU4xTElhdjdvSmlieVN6VTRLVWRBSG1jWUpBUnRSUzlmdUFZQmljNVJmTE5Ddm00WkhyY2tKaEJIdVFxUGV0MDRKdU56RjRma1BzUEswV1NRZEpUZ1JIQS8wP3d4X2ZtdD1naWY

15

发表评论

表情:
评论列表 (有 0 条评论,297人围观)

还没有评论,来说两句吧...

相关阅读

    相关 java序列序列

     Java序列化是指把Java对象转换为字节序列的过程;而Java反序列化是指把字节序列恢复为Java对象的过程  为什么需要序列化与反序列化  我们知道,当两个进程

    相关 java序列序列

    序列化:java对象转化为字节序列,反序列化:字节序列转化为java对象 好处:其好处一是实现了数据的持久化,通过序列化可以把数据永久地保存到硬盘上(通常存放在文件里),

    相关 Java序列序列

    Java序列化与反序列化是什么?为什么需要序列化与反序列化?如何实现Java序列化与反序列化?本文围绕这些问题进行了探讨。  1.Java序列化与反序列化  Java序列化

    相关 Java序列序列

    基本概念: 序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据。 昨天在一

    相关 java 序列序列

    一、什么是序列化与反序列化? > Java 序列化是指把 Java 对象转换为字节序列的过程; > Java 反序列化是指把字节序列恢复为 Java 对象的过程; 二、