java 序列化详解(二)

接着前面的文章说,当任何一个类继承Serializable 这个接口时,Eclipse经常会有黄色惊叹号提示。

提示内容如下:

The serializable class Person does not declare a static final serialVersionUID field of type long

点开以后有2个选择 一个是

Adds a default serial version ID to the selected type.

Use this option to add a user-defined ID in combination with
custom serialization code if the type did undergo structural
changes since its first release.

还有一个是

Adds a generated serial version ID to the selected type.

Use this option to add a compiler-generated ID if the type did
not undergo structural changes since its first release.

那么这个serialVersionUID  是干嘛的?

简单提一下 ,可以把这个

/**
*
*/
private static final long serialVersionUID = 1L;

Eclipse帮我们生成的这个语句解释一下。

实际上,java序列化呢,主要的应用场景就是 rmi。远程接口调用。

我举个例子,前面我们写的那个例子。把她序列化和反序列化 这个过程分开

放在2个工程里面,(要注意虽然是2个工程但是包要一样),

运行以后程序是正常的,但是你要注意 如果此时你2个工程的person类的

serialVersionUID  这个值如果不一样。你就会发现在反序列化的时候失败了。

只有当这个值相等的时候 序列化和反序列化才会成功。

其实这个值主要就是用来强制客户端更新接口用的。(RMI里经常使用)

比如客户端有个类A,服务器端有个类也是A。这个时候2个端用rmi进行通信,

假设服务器端这个类A 修改了某些内容比如增加或删除了一个字段,这个时候你怎么通知

客户端呢,你就把serialVersionUID  这个值修改成和客户端不一样的,这样在序列化或者反序列化的

时候就会出错,如此一来 客户端就知道,噢,序列化出错要更新接口了。

此外,前面的那个文章也说明了,序列化的过程默认调用是 ObjectOutputStream 的 defaultWriteObject 方法以及 ObjectInputStream 的 defaultReadObject 方法

当然我们也可以手动写方法来调用,writeObject 和 readObject 方法可以允许用户控制序列化的过程,手动控制除了能更好的控制序列化所消耗的事件以外,

还有一个优点是可以加密,比如我们要序列化一个 用户的用户名和密码,你默认序列化的话,是有可能被抓取的,但是如果你手动序列化在里面对密码进行

加密,然后在反序列化的时候解密,就非常安全了。

再看一段代码。

 1 package com.burning.test;
 2
 3 import java.io.Serializable;
 4
 5 public class Person implements Serializable {
 6
 7     public static final int STATIC_VALUE =100;
 8
 9
10     @Override
11     public String toString() {
12         return "Person [name=" + name + ", age=" + age + "]";
13     }
14
15     public String getName() {
16         return name;
17     }
18
19     public void setName(String name) {
20         this.name = name;
21     }
22
23     public int getAge() {
24         return age;
25     }
26
27     public void setAge(int age) {
28         this.age = age;
29     }
30
31     private String name;
32
33     private int age;
34
35 }

然后看看main

 1 package com.burning.test;
 2
 3 import java.io.File;
 4 import java.io.FileInputStream;
 5 import java.io.FileNotFoundException;
 6 import java.io.FileOutputStream;
 7 import java.io.IOException;
 8 import java.io.ObjectInputStream;
 9 import java.io.ObjectOutputStream;
10
11 public class TestMain {
12
13     public static void main(String[] args) {
14         // TODO Auto-generated method stub
15         File file = new File("person2.out");
16         ObjectInputStream oin = null;
17         ObjectOutputStream oout = null;
18         try {
19             oout = new ObjectOutputStream(new FileOutputStream(file));
20             Person person = new Person();
21             oout.writeObject(person);
22             oout.flush();
23
24             System.out.println("第1次写完以后" + file.length());
25             oout.writeObject(person);
26             System.out.println("第2次写完以后" + file.length());
27             oout.close();
28             oin = new ObjectInputStream(new FileInputStream(file));
29             Object obj = oin.readObject();
30             Object obj2 = oin.readObject();
31             System.out.println(obj == obj2);
32
33         } catch (FileNotFoundException e) {
34             // TODO Auto-generated catch block
35             e.printStackTrace();
36         } catch (IOException e) {
37             // TODO Auto-generated catch block
38             e.printStackTrace();
39         } catch (ClassNotFoundException e) {
40             // TODO Auto-generated catch block
41             e.printStackTrace();
42         } finally {
43             try {
44                 oout.close();
45             } catch (IOException e) {
46                 // TODO Auto-generated catch block
47                 e.printStackTrace();
48             }
49             try {
50                 oin.close();
51             } catch (IOException e) {
52                 // TODO Auto-generated catch block
53                 e.printStackTrace();
54             }
55
56         }
57
58     }
59 }

运行一下程序。结果为

第1次写完以后83
第2次写完以后88
true。

这个地方运行结果一目了然,我们发现,Java 序列化机制为了节省磁盘空间,具有特定的存储规则,当写入文件的为同一对象时,并不会再将对象的内容进行存储,而只是再次存储一份引用,

上面增加的 5 字节的存储空间就是新增引用和一些控制信息的空间。反序列化时,恢复引用关系,使得清单 3 中的 t1 和 t2 指向唯一的对象,二者相等,输出 true。该存储规则极大的节省了存储空间。

然后我们再修改一下主类

package com.burning.test;

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

public class TestMain {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        File file = new File("person2.out");
        ObjectInputStream oin = null;
        ObjectOutputStream oout = null;
        try {
            oout = new ObjectOutputStream(new FileOutputStream(file));
            Person person = new Person();
            person.setAge(10);
            oout.writeObject(person);
            oout.flush();

            System.out.println("第1次写完以后" + file.length());
            person.setAge(20);
            oout.writeObject(person);
            System.out.println("第2次写完以后" + file.length());
            oout.close();
            oin = new ObjectInputStream(new FileInputStream(file));
            Object obj = oin.readObject();
            Object obj2 = oin.readObject();
            System.out.println(obj.toString());
            System.out.println(obj2.toString());

        } 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();
        } finally {
            try {
                oout.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            try {
                oin.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }

    }
}

看一下运行结果

第1次写完以后83
第2次写完以后88
Person [name=null, age=10]
Person [name=null, age=10]

我们就会发现结果是这样的,因为第一次对象保存完毕以后  你虽然修改了这个对象的值,但是你在第二次序列化对象的时候 因为这2个对象引用相等 所以不会更改值,只会保存一部分引用

所以会得出一个比较奇怪的结果~这个地方要好好理解下

再次修改main 看看运行结果

package com.burning.test;

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

public class TestMain {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        File file = new File("person4.out");
        ObjectInputStream oin = null;
        ObjectOutputStream oout = null;
        try {
            oout = new ObjectOutputStream(new FileOutputStream(file));
            Person person = new Person();
            person.setAge(10);
            oout.writeObject(person);
            oout.flush();

            System.out.println("第1次写完以后" + file.length());
            Person person2 = new Person();
            person2.setAge(20);
            oout.writeObject(person2);
            System.out.println("第2次写完以后" + file.length());
            oout.close();
            oin = new ObjectInputStream(new FileInputStream(file));
            Object obj = oin.readObject();
            Object obj2 = oin.readObject();
            System.out.println(obj.toString());
            System.out.println(obj2.toString());

        } 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();
        } finally {
            try {
                oout.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            try {
                oin.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }

    }
}

这个结果 就是

第1次写完以后83
第2次写完以后94
Person [name=null, age=10]
Person [name=null, age=20]

看完这个结果应该比较好理解了。

时间: 2024-08-26 00:44:04

java 序列化详解(二)的相关文章

spark2.x由浅入深深到底系列六之RDD java api详解二

package com.twq.javaapi.java7; import org.apache.spark.SparkConf; import org.apache.spark.api.java.JavaRDD; import org.apache.spark.api.java.JavaSparkContext; import org.apache.spark.api.java.function.Function2; import org.apache.spark.api.java.funct

Java多线程详解(二)

评论区留下邮箱可获得<Java多线程设计模式详解> 转载请指明来源 1)后台线程 后台线程是为其他线程服务的一种线程,像JVM的垃圾回收线程就是一种后台线程.后台线程总是等到非后台线程死亡之后,后台线程没有了服务对象,不久就会自动死亡,不再复活.利用setDaemon方法可以把一个线程设置为后台线程,但必须在线程启动之前调用. 例如 : /* * @author [email protected] */ public class DaemonThread extends Thread { pu

java序列化详解(一)

所谓对象的序列化,就是让对象可以保存.这里的保存是指保存到本地,你可以理解为文件也可以理解为网络中传输的流~ 今天我们就来讲一下 java序列化的知识,弄懂这个就可以明白android中 对象序列化 ,activity之间对象的传递是怎么一回事. 新建一个person类 package com.burning.test; import java.io.Serializable; public class Person implements Serializable{ public String

Java线程详解(二)

Java线程:新特征-线程池 线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理.当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源. 在使用线程池之前,必须知道如何去创建一个线程池,在Java5中,需要了解的是java.util.concurrent.Executors类的API,这个类提供大量创建连接池的静态方法,是必须掌握的. Java通过Executor

Java 多线程详解(二)------如何创建进程和线程

Java 多线程详解(一)------概念的引入:http://www.cnblogs.com/ysocean/p/6882988.html 在上一篇博客中,我们已经介绍了并发和并行的区别,以及进程和线程的理解,那么在Java 中如何创建进程和线程呢? 1.在 Windows 操作系统中创建进程 在 windows 操作系统中,我们创建一个进程通常就是打开某个应用软件,这便在电脑中创建了一个进程.更原始一点的,我们在命令提示符中来做(我们以打开记事本这个进程为例): 第一步:windows+R,

java关键字详解

Java关键字及其作用 目录 Java关键字及其作用--- 1 一.     关键字总览:2 二.     详细解释--- 3 1.访问控制--- 3 1)私有的-- 3      private 2)受保护的-- 3      protected 3)公共的-- 3      public 2.类.方法和变量修饰符--- 3 1)声明抽象-- 3      abstract 2)类-- 4      class 3)继承.扩展-- 4      extends 4)最终.不可改变-- 4   

Java集合详解7:HashSet,TreeSet与LinkedHashSet

Java集合详解7:HashSet,TreeSet与LinkedHashSet 今天我们来探索一下HashSet,TreeSet与LinkedHashSet的基本原理与源码实现,由于这三个set都是基于之前文章的三个map进行实现的,所以推荐大家先看一下前面有关map的文章,结合使用味道更佳. 具体代码在我的GitHub中可以找到 https://github.com/h2pl/MyTech 文章首发于我的个人博客: https://h2pl.github.io/2018/05/12/colle

package-info.java文件详解

package-info.java文件详解 作者:chszs,转载需注明.博客主页:http://blog.csdn.net/chszs 一.pacakge-info.java介绍 pacakge-info.java是一个Java文件,可以添加到任何的Java源码包中.pacakge-info.java的目标是提供一个包级的文档说明或者是包级的注释. pacakge-info.java文件中,唯一要求包含的内容是包的声明语句,比如: package com.ch.service; 二.包文档 在

Java synchronized详解

Java synchronized详解 第一篇: 使用synchronized 在编写一个类时,如果该类中的代码可能运行于多线程环境下,那么就要考虑同步的问题.在Java中内置了语言级的同步原语--synchronized,这也大大简化了Java中多线程同步的使用.我们首先编写一个非常简单的多线程的程序,是模拟银行中的多个线程同时对同一个储蓄账户进行存款.取款操作的. 在程序中我们使用了一个简化版本的Account类,代表了一个银行账户的信息.在主程序中我们首先生成了1000个线程,然后启动它们