结合自http://swiftlet.net/archives/1268,https://baijiahao.baidu.com/s?id=1622011683975285944&wfr=spider&for=pc
众所周知,类的对象会随着程序的终止而被垃圾收集器销毁。如果要在不重新创建对象的情况下调用该类,该怎么做?这就可以通过序列化将数据转换为字节流。
对象序列化是一个用于将对象状态转换为字节流的过程,可以将其保存到磁盘文件中或通过网络发送到任何其他程序;
从字节流创建对象的相反的过程称为反序列化。而创建的字节流是与平台无关的,在一个平台上序列化的对象可以在不同的平台上反序列化。
1、如何使Java类可序列化?
通过实现java.io.Serializable接口,可以在Java类中启用可序列化。它是一个标记接口,意味着它不包含任何方法或字段,仅用于标识可序列化的语义
2、如果我们试图序列化不可序列化的对象怎么办?
我们将得到一个 RuntimeException 异常:主线程中出现异常 java.io.NotSerializableException。
3、什么是serialVersionUID?
SerialVersionUID是一个标识符,当它通常使用对象的哈希码序列化时会标记在对象上。我们可以通过Java中serialver工具找到该对象的serialVersionUID。
语法:serialver classname,SerialVersionUID用于对象的版本控制。当您添加或修改类中的任何字段时,已经序列化的类将无法恢复,因为serialVersionUID已为新类生成与旧的序列化对象将不同。Java序列化过程依赖于正确的serialVersionUID恢复序列化对象的状态,并在serialVersionUID不匹配时抛出java.io.InvalidClassException 异常。
4、Transient 关键字
transient修饰符仅适用于变量,不适用于方法和类。在序列化时,如果我们不想序列化特定变量以满足安全约束,那么我们应该将该变量声明为transient。执行序列化时,JVM会忽略transient变量的原始值并将默认值保存到文件中。因此,transient意味着不要序列化。
5、Transient 与 Static
静态变量不 是对象状态的一部分,因此它不参与序列化。所以将静态变量声明为transient变量是没有用处的。
6、Final 与 Transient
final变量将直接通过值参与序列化,所以将final变量声明为transient变量不会产生任何影响。
7、serialVersionUID作用:如果我们不希望通过编译来强制划分软件版本,即实现序列化接口的实体能够兼容先前版本,就需要显式地定义一个名为serialVersionUID,类型为long的变量,不修改这个变量值的序列化实体都可以相互进行串行化和反串行化。
serialVersionUID适用于Java的序列化机制。简单来说,Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException。
serialVersionUID有两种显式的生成方式:
一是默认的1L,比如:private static final long serialVersionUID = 1L;
二是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段,比如:
private static final long serialVersionUID = xxxxL;
当实现java.io.Serializable接口的类没有显式地定义一个serialVersionUID变量时候,Java序列化机制会根据编译的Class自动生成一个serialVersionUID作序列化版本比较用,这种情况下,如果Class文件(类名,方法明等)没有发生变化(增加空格,换行,增加注释等等),就算再编译多次,serialVersionUID也不会变化的。如果发生改变,则会变化。
- 情况一:假设Person类序列化之后,从A端传输到B端,然后在B端进行反序列化。在序列化Person和反序列化Person的时候,A端和B端都需要存在一个相同的类。如果两处的serialVersionUID不一致,会产生Exception in thread "main" java.io.InvalidClassException:错误
- 情况二:假设两处serialVersionUID一致,如果A端增加一个字段,然后序列化,而B端不变,然后反序列化,会是什么情况呢?
【答案】新增 public int age; 执行SerialTest,生成序列化文件,代表A端。删除 public int age,反序列化,代表B端,最后的结果为:执行序列化,反序列化正常,但是A端增加的字段丢失(被B端忽略)。
- 情况三:假设两处serialVersionUID一致,如果B端减少一个字段,A端不变,会是什么情况呢?
【答案】序列化,反序列化正常,B端字段少于A端,A端多的字段值丢失(被B端忽略)。
- 情况四:假设两处serialVersionUID一致,如果B端增加一个字段,A端不变,会是什么情况呢?
【答案】序列化,反序列化正常,B端新增加的int字段被赋予了默认值0。

Employee类:
package test.SerialTest;
import java.io.Serializable;
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private transient int salary;
private int add;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
@Override
public String toString(){
return "name = " +name+" ; salary = "+salary;
}
}
序列化测试类:
package test.SerialTest;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Serialize {
public static void main(String[] args) {
Employee e = new Employee();
e.setName("zhaojing");
e.setSalary(15);
try {
ObjectOutputStream ss = new ObjectOutputStream(new FileOutputStream("employeeTest"));
ss.writeObject(e);
ss.close();
}catch (IOException ex){
ex.printStackTrace();
}
}
}
反序列化测试类
package test.SerialTest;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class ReverseSerialize {
public static void main(String[] args) {
try {
ObjectInputStream in = new ObjectInputStream(new FileInputStream("employeeTest"));
Employee employee = (Employee)in.readObject();
in.close();
System.out.println(employee);
}catch (IOException | ClassNotFoundException e){
e.printStackTrace();
}
}
}
1046

被折叠的 条评论
为什么被折叠?



